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
pull/945/head
Ozzieisaacs 6 years ago
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

@ -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")

@ -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\'?')

@ -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">
<div class="form-group"> <label for="config_ldap_provider_url">{{_('LDAP Server Host Name or IP Address')}}</label>
<label for="config_ldap_provider_url">{{_('LDAP Provider URL')}}</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">
<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"> </div>
</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_dn">{{_('LDAP Distinguished Name (DN)')}}</label> <label for="config_ldap_cert_path">{{_('LDAP SSL Certificate Path')}}</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"> <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>
</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/') }}",

@ -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

@ -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…
Cancel
Save