Merge remote-tracking branch 'ldap/master' into Develop
# Conflicts: # cps/server.py # cps/templates/config_edit.html # cps/ub.py # cps/updater.py # cps/web.py # optional-requirements-ldap.txt # setup.cfg
This commit is contained in:
commit
9b74d51f21
|
@ -87,7 +87,11 @@ global_WorkerThread = WorkerThread()
|
||||||
from .server import server
|
from .server import server
|
||||||
Server = server()
|
Server = server()
|
||||||
|
|
||||||
|
from .ldap import Ldap
|
||||||
|
ldap = Ldap()
|
||||||
|
|
||||||
babel = Babel()
|
babel = Babel()
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +106,7 @@ def create_app():
|
||||||
Server.init_app(app)
|
Server.init_app(app)
|
||||||
db.setup_db()
|
db.setup_db()
|
||||||
babel.init_app(app)
|
babel.init_app(app)
|
||||||
|
ldap.init_app(app)
|
||||||
global_WorkerThread.start()
|
global_WorkerThread.start()
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
50
cps/admin.py
50
cps/admin.py
|
@ -397,17 +397,53 @@ def configuration_helper(origin):
|
||||||
|
|
||||||
#LDAP configurator,
|
#LDAP configurator,
|
||||||
if "config_login_type" in to_save and to_save["config_login_type"] == "1":
|
if "config_login_type" in to_save and to_save["config_login_type"] == "1":
|
||||||
if to_save["config_ldap_provider_url"] == u'' or to_save["config_ldap_dn"] == u'':
|
if not to_save["config_ldap_provider_url"] or not to_save["config_ldap_port"] or not to_save["config_ldap_dn"] or not to_save["config_ldap_user_object"]:
|
||||||
ub.session.commit()
|
ub.session.commit()
|
||||||
flash(_(u'Please enter a LDAP provider and a DN'), category="error")
|
flash(_(u'Please enter a LDAP provider, port, DN and user object identifier'), category="error")
|
||||||
return render_title_template("config_edit.html", config=config, origin=origin,
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
gdriveError=gdriveError, feature_support=feature_support,
|
gdrive=gdriveutils.gdrive_support, gdriveError=gdriveError,
|
||||||
title=_(u"Basic Configuration"), page="config")
|
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
||||||
|
page="config")
|
||||||
|
elif not to_save["config_ldap_serv_username"] or not to_save["config_ldap_serv_password"]:
|
||||||
|
ub.session.commit()
|
||||||
|
flash(_(u'Please enter a LDAP service account and password'), category="error")
|
||||||
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
|
gdrive=gdriveutils.gdrive_support, gdriveError=gdriveError,
|
||||||
|
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
||||||
|
page="config")
|
||||||
else:
|
else:
|
||||||
content.config_login_type = constants.LOGIN_LDAP
|
content.config_use_ldap = 1
|
||||||
content.config_ldap_provider_url = to_save["config_ldap_provider_url"]
|
content.config_ldap_provider_url = to_save["config_ldap_provider_url"]
|
||||||
|
content.config_ldap_port = to_save["config_ldap_port"]
|
||||||
|
content.config_ldap_schema = to_save["config_ldap_schema"]
|
||||||
|
content.config_ldap_serv_username = to_save["config_ldap_serv_username"]
|
||||||
|
content.config_ldap_serv_password = base64.b64encode(to_save["config_ldap_serv_password"])
|
||||||
content.config_ldap_dn = to_save["config_ldap_dn"]
|
content.config_ldap_dn = to_save["config_ldap_dn"]
|
||||||
db_change = True
|
content.config_ldap_user_object = to_save["config_ldap_user_object"]
|
||||||
|
reboot_required = True
|
||||||
|
content.config_ldap_use_ssl = 0
|
||||||
|
content.config_ldap_use_tls = 0
|
||||||
|
content.config_ldap_require_cert = 0
|
||||||
|
content.config_ldap_openldap = 0
|
||||||
|
if "config_ldap_use_ssl" in to_save and to_save["config_ldap_use_ssl"] == "on":
|
||||||
|
content.config_ldap_use_ssl = 1
|
||||||
|
if "config_ldap_use_tls" in to_save and to_save["config_ldap_use_tls"] == "on":
|
||||||
|
content.config_ldap_use_tls = 1
|
||||||
|
if "config_ldap_require_cert" in to_save and to_save["config_ldap_require_cert"] == "on":
|
||||||
|
content.config_ldap_require_cert = 1
|
||||||
|
if "config_ldap_openldap" in to_save and to_save["config_ldap_openldap"] == "on":
|
||||||
|
content.config_ldap_openldap = 1
|
||||||
|
if "config_ldap_cert_path " in to_save:
|
||||||
|
if content.config_ldap_cert_path != to_save["config_ldap_cert_path "]:
|
||||||
|
if os.path.isfile(to_save["config_ldap_cert_path "]) or to_save["config_ldap_cert_path "] is u"":
|
||||||
|
content.config_certfile = to_save["config_ldap_cert_path "]
|
||||||
|
else:
|
||||||
|
ub.session.commit()
|
||||||
|
flash(_(u'Certfile location is not valid, please enter correct path'), category="error")
|
||||||
|
return render_title_template("config_edit.html", content=config, origin=origin,
|
||||||
|
gdrive=gdriveutils.gdrive_support, gdriveError=gdriveError,
|
||||||
|
goodreads=goodreads_support, title=_(u"Basic Configuration"),
|
||||||
|
page="config")
|
||||||
|
|
||||||
# Remote login configuration
|
# Remote login configuration
|
||||||
content.config_remote_login = ("config_remote_login" in to_save and to_save["config_remote_login"] == "on")
|
content.config_remote_login = ("config_remote_login" in to_save and to_save["config_remote_login"] == "on")
|
||||||
|
|
61
cps/ldap.py
Normal file
61
cps/ldap.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
|
||||||
|
try:
|
||||||
|
from flask_simpleldap import LDAP, LDAPException
|
||||||
|
ldap_support = True
|
||||||
|
except ImportError:
|
||||||
|
ldap_support = False
|
||||||
|
|
||||||
|
from . import config, logger
|
||||||
|
|
||||||
|
log = logger.create()
|
||||||
|
|
||||||
|
class Ldap():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def init_app(self, app):
|
||||||
|
if ldap_support and config.config_login_type == 1:
|
||||||
|
app.config['LDAP_HOST'] = config.config_ldap_provider_url
|
||||||
|
app.config['LDAP_PORT'] = config.config_ldap_port
|
||||||
|
app.config['LDAP_SCHEMA'] = config.config_ldap_schema
|
||||||
|
app.config['LDAP_USERNAME'] = config.config_ldap_user_object.replace('%s', config.config_ldap_serv_username)\
|
||||||
|
+ ',' + config.config_ldap_dn
|
||||||
|
app.config['LDAP_PASSWORD'] = base64.b64decode(config.config_ldap_serv_password)
|
||||||
|
if config.config_ldap_use_ssl:
|
||||||
|
app.config['LDAP_USE_SSL'] = True
|
||||||
|
if config.config_ldap_use_tls:
|
||||||
|
app.config['LDAP_USE_TLS'] = True
|
||||||
|
app.config['LDAP_REQUIRE_CERT'] = config.config_ldap_require_cert
|
||||||
|
if config.config_ldap_require_cert:
|
||||||
|
app.config['LDAP_CERT_PATH'] = config.config_ldap_cert_path
|
||||||
|
app.config['LDAP_BASE_DN'] = config.config_ldap_dn
|
||||||
|
app.config['LDAP_USER_OBJECT_FILTER'] = config.config_ldap_user_object
|
||||||
|
if config.config_ldap_openldap:
|
||||||
|
app.config['LDAP_OPENLDAP'] = True
|
||||||
|
|
||||||
|
# app.config['LDAP_BASE_DN'] = 'ou=users,dc=yunohost,dc=org'
|
||||||
|
# app.config['LDAP_USER_OBJECT_FILTER'] = '(uid=%s)'
|
||||||
|
# ldap = LDAP(app)
|
||||||
|
|
||||||
|
elif config.config_login_type == 1 and not ldap_support:
|
||||||
|
log.error('Cannot activate ldap support, did you run \'pip install --target vendor -r optional-requirements.txt\'?')
|
57
cps/opds.py
57
cps/opds.py
|
@ -36,6 +36,11 @@ from .helper import fill_indexpage, get_download_link, get_book_cover
|
||||||
from .pagination import Pagination
|
from .pagination import Pagination
|
||||||
from .web import common_filters, get_search_results, render_read_books, download_required
|
from .web import common_filters, get_search_results, render_read_books, download_required
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ldap
|
||||||
|
ldap_support = True
|
||||||
|
except ImportError:
|
||||||
|
ldap_support = False
|
||||||
|
|
||||||
opds = Blueprint('opds', __name__)
|
opds = Blueprint('opds', __name__)
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
@ -52,33 +57,39 @@ def requires_basic_auth_if_no_ano(f):
|
||||||
|
|
||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
|
def basic_auth_required_check(condition):
|
||||||
|
def decorator(f):
|
||||||
|
if condition and ldap_support:
|
||||||
|
return ldap.basic_auth_required(f)
|
||||||
|
return requires_basic_auth_if_no_ano(f)
|
||||||
|
return decorator
|
||||||
|
|
||||||
@opds.route("/opds/")
|
@opds.route("/opds/")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_index():
|
def feed_index():
|
||||||
return render_xml_template('index.xml')
|
return render_xml_template('index.xml')
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/osd")
|
@opds.route("/opds/osd")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_osd():
|
def feed_osd():
|
||||||
return render_xml_template('osd.xml', lang='en-EN')
|
return render_xml_template('osd.xml', lang='en-EN')
|
||||||
|
|
||||||
@opds.route("/opds/search", defaults={'query': ""})
|
@opds.route("/opds/search", defaults={'query': ""})
|
||||||
@opds.route("/opds/search/<query>")
|
@opds.route("/opds/search/<query>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_cc_search(query):
|
def feed_cc_search(query):
|
||||||
return feed_search(query.strip())
|
return feed_search(query.strip())
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/search", methods=["GET"])
|
@opds.route("/opds/search", methods=["GET"])
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_normal_search():
|
def feed_normal_search():
|
||||||
return feed_search(request.args.get("query").strip())
|
return feed_search(request.args.get("query").strip())
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/new")
|
@opds.route("/opds/new")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_new():
|
def feed_new():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -87,7 +98,7 @@ def feed_new():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/discover")
|
@opds.route("/opds/discover")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_discover():
|
def feed_discover():
|
||||||
entries = db.session.query(db.Books).filter(common_filters()).order_by(func.random())\
|
entries = db.session.query(db.Books).filter(common_filters()).order_by(func.random())\
|
||||||
.limit(config.config_books_per_page)
|
.limit(config.config_books_per_page)
|
||||||
|
@ -96,7 +107,7 @@ def feed_discover():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/rated")
|
@opds.route("/opds/rated")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_best_rated():
|
def feed_best_rated():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -105,7 +116,7 @@ def feed_best_rated():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/hot")
|
@opds.route("/opds/hot")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_hot():
|
def feed_hot():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)).order_by(
|
all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)).order_by(
|
||||||
|
@ -130,7 +141,7 @@ def feed_hot():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/author")
|
@opds.route("/opds/author")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_authorindex():
|
def feed_authorindex():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries = db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\
|
entries = db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\
|
||||||
|
@ -141,7 +152,7 @@ def feed_authorindex():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/author/<int:book_id>")
|
@opds.route("/opds/author/<int:book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_author(book_id):
|
def feed_author(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -150,7 +161,7 @@ def feed_author(book_id):
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/publisher")
|
@opds.route("/opds/publisher")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_publisherindex():
|
def feed_publisherindex():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\
|
entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\
|
||||||
|
@ -161,7 +172,7 @@ def feed_publisherindex():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/publisher/<int:book_id>")
|
@opds.route("/opds/publisher/<int:book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_publisher(book_id):
|
def feed_publisher(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -171,7 +182,7 @@ def feed_publisher(book_id):
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/category")
|
@opds.route("/opds/category")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_categoryindex():
|
def feed_categoryindex():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters())\
|
entries = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters())\
|
||||||
|
@ -182,7 +193,7 @@ def feed_categoryindex():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/category/<int:book_id>")
|
@opds.route("/opds/category/<int:book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_category(book_id):
|
def feed_category(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -191,7 +202,7 @@ def feed_category(book_id):
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/series")
|
@opds.route("/opds/series")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_seriesindex():
|
def feed_seriesindex():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries = db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters())\
|
entries = db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters())\
|
||||||
|
@ -202,7 +213,7 @@ def feed_seriesindex():
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/series/<int:book_id>")
|
@opds.route("/opds/series/<int:book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_series(book_id):
|
def feed_series(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||||
|
@ -212,7 +223,7 @@ def feed_series(book_id):
|
||||||
|
|
||||||
@opds.route("/opds/shelfindex/", defaults={'public': 0})
|
@opds.route("/opds/shelfindex/", defaults={'public': 0})
|
||||||
@opds.route("/opds/shelfindex/<string:public>")
|
@opds.route("/opds/shelfindex/<string:public>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_shelfindex(public):
|
def feed_shelfindex(public):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
if public is not 0:
|
if public is not 0:
|
||||||
|
@ -227,7 +238,7 @@ def feed_shelfindex(public):
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/shelf/<int:book_id>")
|
@opds.route("/opds/shelf/<int:book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_shelf(book_id):
|
def feed_shelf(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
if current_user.is_anonymous:
|
if current_user.is_anonymous:
|
||||||
|
@ -251,14 +262,14 @@ def feed_shelf(book_id):
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/download/<book_id>/<book_format>/")
|
@opds.route("/opds/download/<book_id>/<book_format>/")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
@download_required
|
@download_required
|
||||||
def opds_download_link(book_id, book_format):
|
def opds_download_link(book_id, book_format):
|
||||||
return get_download_link(book_id,book_format)
|
return get_download_link(book_id,book_format)
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/ajax/book/<string:uuid>")
|
@opds.route("/ajax/book/<string:uuid>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def get_metadata_calibre_companion(uuid):
|
def get_metadata_calibre_companion(uuid):
|
||||||
entry = db.session.query(db.Books).filter(db.Books.uuid.like("%" + uuid + "%")).first()
|
entry = db.session.query(db.Books).filter(db.Books.uuid.like("%" + uuid + "%")).first()
|
||||||
if entry is not None:
|
if entry is not None:
|
||||||
|
@ -307,19 +318,19 @@ def render_xml_template(*args, **kwargs):
|
||||||
@opds.route("/opds/cover_240_240/<book_id>")
|
@opds.route("/opds/cover_240_240/<book_id>")
|
||||||
@opds.route("/opds/cover_90_90/<book_id>")
|
@opds.route("/opds/cover_90_90/<book_id>")
|
||||||
@opds.route("/opds/cover/<book_id>")
|
@opds.route("/opds/cover/<book_id>")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_get_cover(book_id):
|
def feed_get_cover(book_id):
|
||||||
return get_book_cover(book_id)
|
return get_book_cover(book_id)
|
||||||
|
|
||||||
@opds.route("/opds/readbooks/")
|
@opds.route("/opds/readbooks/")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_read_books():
|
def feed_read_books():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True)
|
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True)
|
||||||
|
|
||||||
|
|
||||||
@opds.route("/opds/unreadbooks/")
|
@opds.route("/opds/unreadbooks/")
|
||||||
@requires_basic_auth_if_no_ano
|
@basic_auth_required_check(config.config_login_type)
|
||||||
def feed_unread_books():
|
def feed_unread_books():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True)
|
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True)
|
||||||
|
|
|
@ -197,18 +197,59 @@
|
||||||
<option value="3" {% if config.config_login_type == 3 %}selected{% endif %}>{{_('Use Google OAuth')}}</option>
|
<option value="3" {% if config.config_login_type == 3 %}selected{% endif %}>{{_('Use Google OAuth')}}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
{% if feature_support['ldap'] %}
|
||||||
{% if feature_support['ldap'] %}
|
<div data-related="login-settings-1">
|
||||||
<div data-related="login-settings-1">
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_provider_url">{{_('LDAP Server Host Name or IP Address')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_provider_url" name="config_ldap_provider_url" value="{% if content.config_ldap_provider_url != None %}{{ content.config_ldap_provider_url }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_port">{{_('LDAP Server Port')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_port" name="config_ldap_port" value="{% if content.config_ldap_port != None %}{{ content.config_ldap_port }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_schema">{{_('LDAP schema (ldap or ldaps)')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_schema" name="config_ldap_schema" value="{% if content.config_ldap_schema != None %}{{ content.config_ldap_schema }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_serv_username">{{_('LDAP Admin username')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_serv_username" name="config_ldap_serv_username" value="{% if content.config_ldap_serv_username != None %}{{ content.config_ldap_serv_username }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_serv_password">{{_('LDAP Admin password')}}</label>
|
||||||
|
<input type="password" class="form-control" id="config_ldap_serv_password" name="config_ldap_serv_password" value="{% if content.config_ldap_serv_password != None %}{{ content.config_ldap_serv_password }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="config_ldap_use_ssl" name="config_ldap_use_ssl" {% if content.config_ldap_use_ssl %}checked{% endif %}>
|
||||||
|
<label for="config_ldap_ssl">{{_('LDAP Server use SSL')}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="config_ldap_use_tls" name="config_ldap_use_tls" {% if content.config_ldap_use_tls %}checked{% endif %}>
|
||||||
|
<label for="config_ldap_ssl">{{_('LDAP Server use TLS')}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="config_ldap_require_cert" name="config_ldap_require_cert" data-control="ldap-cert-settings" {% if content.config_ldap_require_cert %}checked{% endif %}>
|
||||||
|
<label for="config_ldap_ssl">{{_('LDAP Server Certificate')}}</label>
|
||||||
|
</div>
|
||||||
|
<div data-related="ldap-cert-settings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="config_ldap_provider_url">{{_('LDAP Provider URL')}}</label>
|
<label for="config_ldap_cert_path">{{_('LDAP SSL Certificate Path')}}</label>
|
||||||
<input type="text" class="form-control" id="config_ldap_provider_url" name="config_ldap_provider_url" value="{% if config.config_ldap_provider_url != None %}{{ config.config_ldap_provider_url }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if content.config_ldap_cert_path != None and content.config_ldap_require_cert !=None %}{{ content.config_ldap_cert_path }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="config_ldap_dn">{{_('LDAP Distinguished Name (DN)')}}</label>
|
|
||||||
<input type="text" class="form-control" id="config_ldap_dn" name="config_ldap_dn" value="{% if config.config_ldap_dn != None %}{{ config.config_ldap_dn }}{% endif %}" autocomplete="off">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_dn">{{_('LDAP Distinguished Name (DN)')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_dn" name="config_ldap_dn" value="{% if content.config_ldap_dn != None %}{{ content.config_ldap_dn }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_ldap_user_object">{{_('LDAP User object filter')}}</label>
|
||||||
|
<input type="text" class="form-control" id="config_ldap_user_object" name="config_ldap_user_object" value="{% if content.config_ldap_user_object != None %}{{ content.config_ldap_user_object }}{% endif %}" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="config_ldap_openldap" name="config_ldap_openldap" {% if content.config_ldap_openldap %}checked{% endif %}>
|
||||||
|
<label for="config_ldap_openldap">{{_('LDAP Server is OpenLDAP?')}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if feature_support['oauth'] %}
|
{% if feature_support['oauth'] %}
|
||||||
<div data-related="login-settings-2">
|
<div data-related="login-settings-2">
|
||||||
|
|
|
@ -79,6 +79,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.calibre = {
|
||||||
|
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
||||||
|
cssPath: "{{ url_for('static', filename='css/') }}",
|
||||||
|
bookmarkUrl: "{{ url_for('bookmark', book_id=bookid, book_format='EPUB') }}",
|
||||||
|
bookUrl: "{{ url_for('get_download_link_ext', book_id=bookid, book_format="epub", anyname='file.epub') }}",
|
||||||
|
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||||
|
useBookmarks: {{ g.user.is_authenticated | tojson }} };
|
||||||
|
</script>
|
||||||
|
<script src="{{ url_for('static', filename='js/libs/jszip.min.js') }}">
|
||||||
|
</script> <script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.calibre = {
|
window.calibre = {
|
||||||
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
||||||
|
|
82
cps/ub.py
82
cps/ub.py
|
@ -354,6 +354,18 @@ class Settings(Base):
|
||||||
# config_use_google_oauth = Column(Boolean)
|
# config_use_google_oauth = Column(Boolean)
|
||||||
config_google_oauth_client_id = Column(String)
|
config_google_oauth_client_id = Column(String)
|
||||||
config_google_oauth_client_secret = Column(String)
|
config_google_oauth_client_secret = Column(String)
|
||||||
|
config_ldap_provider_url = Column(String, default='localhost')
|
||||||
|
config_ldap_port = Column(SmallInteger, default=389)
|
||||||
|
config_ldap_schema = Column(String, default='ldap')
|
||||||
|
config_ldap_serv_username = Column(String)
|
||||||
|
config_ldap_serv_password = Column(String)
|
||||||
|
config_ldap_use_ssl = Column(Boolean, default=False)
|
||||||
|
config_ldap_use_tls = Column(Boolean, default=False)
|
||||||
|
config_ldap_require_cert = Column(Boolean, default=False)
|
||||||
|
config_ldap_cert_path = Column(String)
|
||||||
|
config_ldap_dn = Column(String)
|
||||||
|
config_ldap_user_object = Column(String)
|
||||||
|
config_ldap_openldap = Column(Boolean)
|
||||||
config_mature_content_tags = Column(String)
|
config_mature_content_tags = Column(String)
|
||||||
config_logfile = Column(String)
|
config_logfile = Column(String)
|
||||||
config_access_logfile = Column(String)
|
config_access_logfile = Column(String)
|
||||||
|
@ -431,7 +443,17 @@ class Config:
|
||||||
self.config_goodreads_api_secret = data.config_goodreads_api_secret
|
self.config_goodreads_api_secret = data.config_goodreads_api_secret
|
||||||
self.config_login_type = data.config_login_type
|
self.config_login_type = data.config_login_type
|
||||||
# self.config_use_ldap = data.config_use_ldap
|
# self.config_use_ldap = data.config_use_ldap
|
||||||
|
self.config_ldap_user_object = data.config_ldap_user_object
|
||||||
|
self.config_ldap_openldap = data.config_ldap_openldap
|
||||||
self.config_ldap_provider_url = data.config_ldap_provider_url
|
self.config_ldap_provider_url = data.config_ldap_provider_url
|
||||||
|
self.config_ldap_port = data.config_ldap_port
|
||||||
|
self.config_ldap_schema = data.config_ldap_schema
|
||||||
|
self.config_ldap_serv_username = data.config_ldap_serv_username
|
||||||
|
self.config_ldap_serv_password = data.config_ldap_serv_password
|
||||||
|
self.config_ldap_use_ssl = data.config_ldap_use_ssl
|
||||||
|
self.config_ldap_use_tls = data.config_ldap_use_ssl
|
||||||
|
self.config_ldap_require_cert = data.config_ldap_require_cert
|
||||||
|
self.config_ldap_cert_path = data.config_ldap_cert_path
|
||||||
self.config_ldap_dn = data.config_ldap_dn
|
self.config_ldap_dn = data.config_ldap_dn
|
||||||
# self.config_use_github_oauth = data.config_use_github_oauth
|
# self.config_use_github_oauth = data.config_use_github_oauth
|
||||||
self.config_github_oauth_client_id = data.config_github_oauth_client_id
|
self.config_github_oauth_client_id = data.config_github_oauth_client_id
|
||||||
|
@ -676,11 +698,71 @@ def migrate_Database():
|
||||||
except exc.OperationalError:
|
except exc.OperationalError:
|
||||||
conn = engine.connect()
|
conn = engine.connect()
|
||||||
conn.execute("ALTER TABLE Settings ADD column `config_login_type` INTEGER DEFAULT 0")
|
conn.execute("ALTER TABLE Settings ADD column `config_login_type` INTEGER DEFAULT 0")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_provider_url)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
conn.execute("ALTER TABLE Settings ADD column `config_ldap_provider_url` String DEFAULT ''")
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_provider_url` String DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_port)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_port` INTEGER DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_schema)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_schema` String DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_serv_username)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_serv_username` String DEFAULT ''")
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_serv_password` String DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_use_ssl)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_use_ssl` INTEGER DEFAULT 0")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_use_tls)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_use_tls` INTEGER DEFAULT 0")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_require_cert)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_require_cert` INTEGER DEFAULT 0")
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_cert_path` String DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_dn)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
conn.execute("ALTER TABLE Settings ADD column `config_ldap_dn` String DEFAULT ''")
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_dn` String DEFAULT ''")
|
||||||
conn.execute("ALTER TABLE Settings ADD column `config_github_oauth_client_id` String DEFAULT ''")
|
conn.execute("ALTER TABLE Settings ADD column `config_github_oauth_client_id` String DEFAULT ''")
|
||||||
conn.execute("ALTER TABLE Settings ADD column `config_github_oauth_client_secret` String DEFAULT ''")
|
conn.execute("ALTER TABLE Settings ADD column `config_github_oauth_client_secret` String DEFAULT ''")
|
||||||
session.commit()
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_user_object)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_user_object` String DEFAULT ''")
|
||||||
|
session.commit()
|
||||||
|
try:
|
||||||
|
session.query(exists().where(Settings.config_ldap_openldap)).scalar()
|
||||||
|
except exc.OperationalError:
|
||||||
|
conn = engine.connect()
|
||||||
|
conn.execute("ALTER TABLE Settings ADD column `config_ldap_openldap` INTEGER DEFAULT 0")
|
||||||
|
session.commit()
|
||||||
try:
|
try:
|
||||||
session.query(exists().where(Settings.config_theme)).scalar()
|
session.query(exists().where(Settings.config_theme)).scalar()
|
||||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||||
|
|
27
cps/web.py
27
cps/web.py
|
@ -103,7 +103,6 @@ for ex in default_exceptions:
|
||||||
app.register_error_handler(ex, error_http)
|
app.register_error_handler(ex, error_http)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
web = Blueprint('web', __name__)
|
web = Blueprint('web', __name__)
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
@ -244,9 +243,7 @@ def before_request():
|
||||||
return redirect(url_for('admin.basic_configuration'))
|
return redirect(url_for('admin.basic_configuration'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ################################### data provider functions #########################################################
|
# ################################### data provider functions #########################################################
|
||||||
|
|
||||||
@web.route("/ajax/emailstat")
|
@web.route("/ajax/emailstat")
|
||||||
@login_required
|
@login_required
|
||||||
def get_email_status_json():
|
def get_email_status_json():
|
||||||
|
@ -1082,24 +1079,32 @@ def login():
|
||||||
return redirect(url_for('admin.basic_configuration'))
|
return redirect(url_for('admin.basic_configuration'))
|
||||||
if current_user is not None and current_user.is_authenticated:
|
if current_user is not None and current_user.is_authenticated:
|
||||||
return redirect(url_for('web.index'))
|
return redirect(url_for('web.index'))
|
||||||
|
if config.config_login_type == 1 and not feature_support['ldap']:
|
||||||
|
flash(_(u"Cannot activate LDAP authentication"), category="error")
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = request.form.to_dict()
|
form = request.form.to_dict()
|
||||||
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == form['username'].strip().lower())\
|
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == form['username'].strip().lower())\
|
||||||
.first()
|
.first()
|
||||||
if config.config_login_type == 1 and user:
|
if config.config_login_type == 1 and user and feature_support['ldap']:
|
||||||
try:
|
try:
|
||||||
ub.User.try_login(form['username'], form['password'], config.config_ldap_dn,
|
if ldap.bind_user(form['username'], form['password']) is not None:
|
||||||
config.config_ldap_provider_url)
|
login_user(user, remember=True)
|
||||||
login_user(user, remember=True)
|
flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname),
|
||||||
flash(_(u"You are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success")
|
category="success")
|
||||||
return redirect_back(url_for("web.index"))
|
return redirect_back(url_for("web.index"))
|
||||||
except ldap.INVALID_CREDENTIALS:
|
except ldap.INVALID_CREDENTIALS as e:
|
||||||
|
log.error('Login Error: ' + str(e))
|
||||||
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
log.info('LDAP Login failed for user "%s" IP-adress: %s', form['username'], ipAdress)
|
log.info('LDAP Login failed for user "%s" IP-adress: %s', form['username'], ipAdress)
|
||||||
flash(_(u"Wrong Username or Password"), category="error")
|
flash(_(u"Wrong Username or Password"), category="error")
|
||||||
except ldap.SERVER_DOWN:
|
except ldap.SERVER_DOWN:
|
||||||
log.info('LDAP Login failed, LDAP Server down')
|
log.info('LDAP Login failed, LDAP Server down')
|
||||||
flash(_(u"Could not login. LDAP server down, please contact your administrator"), category="error")
|
flash(_(u"Could not login. LDAP server down, please contact your administrator"), category="error")
|
||||||
|
'''except LDAPException as exception:
|
||||||
|
app.logger.error('Login Error: ' + str(exception))
|
||||||
|
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
|
app.logger.info('LDAP Login failed for user "' + form['username'] + ', IP-address :' + ipAdress)
|
||||||
|
flash(_(u"Wrong Username or Password"), category="error")'''
|
||||||
else:
|
else:
|
||||||
if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest":
|
if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest":
|
||||||
login_user(user, remember=True)
|
login_user(user, remember=True)
|
||||||
|
@ -1274,8 +1279,10 @@ def profile():
|
||||||
page="me", registered_oauth=oauth_check, oauth_status=oauth_status)
|
page="me", registered_oauth=oauth_check, oauth_status=oauth_status)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ###################################Show single book ##################################################################
|
# ###################################Show single book ##################################################################
|
||||||
|
|
||||||
|
|
||||||
@web.route("/read/<int:book_id>/<book_format>")
|
@web.route("/read/<int:book_id>/<book_format>")
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
@viewer_required
|
@viewer_required
|
||||||
|
|
|
@ -18,6 +18,7 @@ python-Levenshtein>=0.12.0
|
||||||
|
|
||||||
# ldap login
|
# ldap login
|
||||||
python_ldap>=3.0.0
|
python_ldap>=3.0.0
|
||||||
|
flask-simpleldap
|
||||||
|
|
||||||
# extracting metadata
|
# extracting metadata
|
||||||
lxml>=3.8.0
|
lxml>=3.8.0
|
||||||
|
|
|
@ -81,6 +81,6 @@ metadata =
|
||||||
comics=
|
comics=
|
||||||
natsort>=2.2.0
|
natsort>=2.2.0
|
||||||
# https://github.com/wildthyme/comicapi/archive/cb279168f9c5cec742b5a05ac8326b9c168a8a91.zip#egg=comicapi
|
# https://github.com/wildthyme/comicapi/archive/cb279168f9c5cec742b5a05ac8326b9c168a8a91.zip#egg=comicapi
|
||||||
|
# comicapi @ git+https://github.com/wildthyme/comicapi.git@cb279168f9c5cec742b5a05ac8326b9c168a8a91#egg=comicapi
|
||||||
# find solution for this
|
# find solution for this
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user