diff --git a/cps/admin.py b/cps/admin.py index f0e90e5c..1b8824ca 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -38,7 +38,7 @@ from sqlalchemy.exc import IntegrityError from gdriveutils import is_gdrive_ready, gdrive_support, downloadFile, deleteDatabaseOnChange, listRootFolders import helper from werkzeug.security import generate_password_hash -from sqlalchemy.sql.expression import text +from oauth_bb import oauth_check try: from goodreads.client import GoodreadsClient @@ -591,7 +591,7 @@ def new_user(): content.sidebar_view = config.config_default_show content.mature_content = bool(config.config_default_show & ub.MATURE_CONTENT) return render_title_template("user_edit.html", new_user=1, content=content, translations=translations, - languages=languages, title=_(u"Add new user"), page="newuser") + languages=languages, title=_(u"Add new user"), page="newuser", registered_oauth=oauth_check) @admi.route("/admin/mailsettings", methods=["GET", "POST"]) @@ -767,7 +767,7 @@ def edit_user(user_id): flash(_(u"An unknown error occured."), category="error") return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0, content=content, downloads=downloads, title=_(u"Edit User %(nick)s", - nick=content.nickname), page="edituser") + nick=content.nickname), page="edituser", registered_oauth=oauth_check) @admi.route("/admin/resetpassword/") diff --git a/cps/ldap.py b/cps/ldap.py new file mode 100644 index 00000000..93995b3e --- /dev/null +++ b/cps/ldap.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web) +# Copyright (C) 2018-2019 Krakinou +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import ldap +from cps import ub, app, request +from flask import flash, url_for +from redirect import redirect_back +from flask_login import login_user +from flask_babel import gettext as _ + +def login(form, user): + try: + ub.User.try_login(form['username'], form['password']) + login_user(user, remember=True) + flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") + return redirect_back(url_for("web.index")) + except ldap.INVALID_CREDENTIALS: + ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) + app.logger.info('LDAP Login failed for user "' + form['username'] + '" IP-adress: ' + ipAdress) + flash(_(u"Wrong Username or Password"), category="error") + +def logout(): + pass diff --git a/cps/oauth_bb.py b/cps/oauth_bb.py index 0be03617..7cfe1d92 100644 --- a/cps/oauth_bb.py +++ b/cps/oauth_bb.py @@ -25,16 +25,33 @@ from flask_dance.contrib.google import make_google_blueprint, google from flask_dance.consumer import oauth_authorized, oauth_error from sqlalchemy.orm.exc import NoResultFound from oauth import OAuthBackend -from flask import flash, session, redirect, url_for, request +from flask import flash, session, redirect, url_for, request, make_response, abort +import json from cps import config, app import ub from flask_login import login_user, login_required, current_user from flask_babel import gettext as _ -from web import github_oauth_required +# from web import github_oauth_required +from functools import wraps oauth_check = {} +def github_oauth_required(f): + @wraps(f) + def inner(*args, **kwargs): + if config.config_use_github_oauth: + return f(*args, **kwargs) + if request.is_xhr: + data = {'status': 'error', 'message': 'Not Found'} + response = make_response(json.dumps(data, ensure_ascii=False)) + response.headers["Content-Type"] = "application/json; charset=utf-8" + return response, 404 + abort(404) + + return inner + + def register_oauth_blueprint(blueprint, show_name): if blueprint.name != "": oauth_check[blueprint.name] = show_name @@ -246,7 +263,7 @@ def github_error(blueprint, error, error_description=None, error_uri=None): ) flash(msg, category="error") - +''' @oauth.route('/github') @github_oauth_required def github_login(): @@ -277,7 +294,7 @@ def google_login(): return bind_oauth_or_register(google_blueprint.name, account_info_json['id'], 'google.login') flash(_(u"Google Oauth error, please retry later."), category="error") return redirect(url_for('login')) - +''' @oauth_error.connect_via(google_blueprint) def google_error(blueprint, error, error_description=None, error_uri=None): @@ -292,8 +309,8 @@ def google_error(blueprint, error, error_description=None, error_uri=None): ) flash(msg, category="error") - +''' @oauth.route('/unlink/google', methods=["GET"]) @login_required def google_login_unlink(): - return unlink_oauth(google_blueprint.name) + return unlink_oauth(google_blueprint.name)''' diff --git a/cps/ub.py b/cps/ub.py index fa8a86e5..59f0b613 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -23,6 +23,7 @@ from sqlalchemy import exc from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import * from flask_login import AnonymousUserMixin +from flask_dance.consumer.backend.sqla import OAuthConsumerMixin import sys import os import logging @@ -32,6 +33,11 @@ import datetime from binascii import hexlify import cli +try: + import ldap +except ImportError: + pass + engine = create_engine('sqlite:///{0}'.format(cli.settingspath), echo=False) Base = declarative_base() @@ -176,13 +182,12 @@ class UserBase: return '' % self.nickname #Login via LDAP method - ''''@staticmethod + @staticmethod def try_login(username, password): conn = get_ldap_connection() conn.simple_bind_s( config.config_ldap_dn.replace("%s", username), - password - )''' + password) # Baseclass for Users in Calibre-Web, settings which are depending on certain users are stored here. It is derived from # User Base (all access methods are declared there) @@ -202,11 +207,11 @@ class User(UserBase, Base): default_language = Column(String(3), default="all") mature_content = Column(Boolean, default=True) -''' + class OAuth(OAuthConsumerMixin, Base): provider_user_id = Column(String(256)) user_id = Column(Integer, ForeignKey(User.id)) - user = relationship(User)''' + user = relationship(User) # Class for anonymous user is derived from User base and completly overrides methods and properties for the @@ -776,6 +781,13 @@ def clean_database(): session.query(RemoteAuthToken).filter(now > RemoteAuthToken.expiration).delete() +#get LDAP connection +def get_ldap_connection(): + conn = ldap.initialize('ldap://{}'.format(config.config_ldap_provider_url)) + return conn + + + def create_default_config(): settings = Settings() settings.mail_server = "mail.example.com" diff --git a/cps/web.py b/cps/web.py index ae577e7e..91cc8ff0 100644 --- a/cps/web.py +++ b/cps/web.py @@ -26,8 +26,6 @@ from flask import render_template, request, redirect, url_for, send_from_directo from werkzeug.exceptions import default_exceptions import helper import os -# from sqlalchemy.sql.expression import func -# from sqlalchemy.sql.expression import false from sqlalchemy.exc import IntegrityError from flask_login import login_user, logout_user, login_required, current_user from flask_babel import gettext as _ @@ -36,21 +34,31 @@ from werkzeug.datastructures import Headers from babel import Locale as LC from babel.dates import format_date from babel.core import UnknownLocaleError -from functools import wraps import base64 from sqlalchemy.sql import * import json import datetime from iso639 import languages as isoLanguages -import os.path import re import db import gdriveutils from redirect import redirect_back from cps import lm, babel, ub, config, get_locale, language_table, app from pagination import Pagination -# from admin import check_valid_domain -# from oauth_bb import oauth_check, register_user_with_oauth +from sqlalchemy.sql.expression import text + +from oauth_bb import oauth_check, register_user_with_oauth, logout_oauth_user, get_oauth_status + +'''try: + oauth_support = True +except ImportError: + oauth_support = False''' + +try: + import ldap + ldap_support = True +except ImportError: + ldap_support = False try: from googleapiclient.errors import HttpError @@ -70,7 +78,7 @@ except ImportError: levenshtein_support = False try: - from functools import reduce + from functools import reduce, wraps except ImportError: pass # We're not using Python 3 @@ -169,21 +177,6 @@ def remote_login_required(f): return inner -def github_oauth_required(f): - @wraps(f) - def inner(*args, **kwargs): - if config.config_use_github_oauth: - return f(*args, **kwargs) - if request.is_xhr: - data = {'status': 'error', 'message': 'Not Found'} - response = make_response(json.dumps(data, ensure_ascii=False)) - response.headers["Content-Type"] = "application/json; charset=utf-8" - return response, 404 - abort(404) - - return inner - - def google_oauth_required(f): @wraps(f) def inner(*args, **kwargs): @@ -1299,7 +1292,8 @@ def register(): try: ub.session.add(content) ub.session.commit() - # register_user_with_oauth(content) + if oauth_support: + register_user_with_oauth(content) helper.send_registration_mail(to_save["email"], to_save["nickname"], password) except Exception: ub.session.rollback() @@ -1316,7 +1310,8 @@ def register(): flash(_(u"This username or e-mail address is already in use."), category="error") return render_title_template('register.html', title=_(u"register"), page="register") - # register_user_with_oauth() + if oauth_support: + register_user_with_oauth() return render_title_template('register.html', config=config, title=_(u"register"), page="register") @@ -1330,7 +1325,7 @@ def login(): form = request.form.to_dict() user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == form['username'].strip().lower())\ .first() - if config.config_use_ldap and user: + '''if config.config_use_ldap and user: import ldap try: ub.User.try_login(form['username'], form['password']) @@ -1341,7 +1336,8 @@ def login(): ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) app.logger.info('LDAP Login failed for user "' + form['username'] + '" IP-adress: ' + ipAdress) flash(_(u"Wrong Username or Password"), category="error") - elif user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest": + el''' + if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest": login_user(user, remember=True) flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") return redirect_back(url_for("web.index")) @@ -1362,7 +1358,8 @@ def login(): def logout(): if current_user is not None and current_user.is_authenticated: logout_user() - # logout_oauth_user() + if oauth_support: + logout_oauth_user() return redirect(url_for('web.login')) @@ -1475,7 +1472,7 @@ def profile(): downloads = list() languages = speaking_language() translations = babel.list_translations() + [LC('en')] - oauth_status = None # oauth_status = get_oauth_status() + oauth_status = get_oauth_status() for book in content.downloads: downloadBook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() if downloadBook: