From d6e7314f05f8a2702efab925c33146a7c1e1b5e8 Mon Sep 17 00:00:00 2001
From: Dimitri Huisman
Date: Wed, 25 Jan 2023 15:26:10 +0000
Subject: [PATCH 1/3] Make API configurable via the setup utility Fix some
small bugs in the setup utility Improve documentation on the API.
---
core/admin/mailu/__init__.py | 3 +--
core/admin/mailu/api/__init__.py | 32 ++++++++++++++---------
docs/api.rst | 20 ++++++++-------
docs/configuration.rst | 14 +++++------
setup/flavors/compose/mailu.env | 10 ++++++++
setup/static/render.js | 42 +++++++++++++++++++++++++------
setup/templates/steps/config.html | 13 ++++++++++
7 files changed, 96 insertions(+), 38 deletions(-)
diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py
index de3503d0..e29eff91 100644
--- a/core/admin/mailu/__init__.py
+++ b/core/admin/mailu/__init__.py
@@ -74,8 +74,7 @@ def create_app_from_config(config):
app.register_blueprint(ui.ui, url_prefix=app.config['WEB_ADMIN'])
app.register_blueprint(internal.internal, url_prefix='/internal')
app.register_blueprint(sso.sso, url_prefix='/sso')
- if app.config.get('API_TOKEN'):
- api.register(app, web_api_root=app.config.get('WEB_API'))
+ api.register(app, web_api_root=app.config.get('WEB_API'))
return app
diff --git a/core/admin/mailu/api/__init__.py b/core/admin/mailu/api/__init__.py
index da7325ae..0465c783 100644
--- a/core/admin/mailu/api/__init__.py
+++ b/core/admin/mailu/api/__init__.py
@@ -8,17 +8,25 @@ def register(app, web_api_root):
# register api bluprint(s)
apidoc.apidoc.url_prefix = f'{web_api_root}/v{int(APIv1.VERSION)}'
APIv1.api_token = app.config['API_TOKEN']
- app.register_blueprint(APIv1.blueprint, url_prefix=f'{web_api_root}/v{int(APIv1.VERSION)}')
+ if app.config['API_TOKEN'] != '':
+ app.register_blueprint(APIv1.blueprint, url_prefix=f'{web_api_root}/v{int(APIv1.VERSION)}')
- # add redirect to current api version
- redirect_api = Blueprint('redirect_api', __name__)
- @redirect_api.route('/')
- def redir():
- return redirect(url_for(f'{APIv1.blueprint.name}.root'))
- app.register_blueprint(redirect_api, url_prefix=f'{web_api_root}')
+ # add redirect to current api version
+ redirect_api = Blueprint('redirect_api', __name__)
+ @redirect_api.route('/')
+ def redir():
+ return redirect(url_for(f'{APIv1.blueprint.name}.root'))
+ app.register_blueprint(redirect_api, url_prefix=f'{web_api_root}')
- # swagger ui config
- app.config.SWAGGER_UI_DOC_EXPANSION = 'list'
- app.config.SWAGGER_UI_OPERATION_ID = True
- app.config.SWAGGER_UI_REQUEST_DURATION = True
- app.config.RESTX_MASK_SWAGGER = False
+ # swagger ui config
+ app.config.SWAGGER_UI_DOC_EXPANSION = 'list'
+ app.config.SWAGGER_UI_OPERATION_ID = True
+ app.config.SWAGGER_UI_REQUEST_DURATION = True
+ app.config.RESTX_MASK_SWAGGER = False
+ else:
+ api = Blueprint('api', __name__)
+ @api.route('/', defaults={'path': ''})
+ @api.route('/')
+ def api_token_missing(path):
+ return "
Error: API_TOKEN is not configured
", 500
+ app.register_blueprint(api, url_prefix=f'{web_api_root}')
diff --git a/docs/api.rst b/docs/api.rst
index b18398ac..e5a18a03 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -5,21 +5,22 @@ Mailu offers a RESTful API for changing the Mailu configuration.
Anything that can be configured via the Mailu web administration interface,
can also be configured via the API.
-The Mailu API is disabled by default. It can be enabled and configured via
-the settings:
+The Mailu API can be configured via the setup utility (setup.mailu.io).
+It can also be manually configured via mailu.env:
-* ``API``
-* ``WEB_API``
-* ``API_TOKEN``
+* ``API`` - Expose the API interface (value: true, false)
+* ``WEB_API`` - Path to the API interface
+* ``API_TOKEN`` - API token for authentication
-For more information see the section :ref:`Advanced configuration `
-in the configuration reference.
+For more information refer to the detailed descriptions in the
+:ref:`configuration reference `.
Swagger.json
------------
-The swagger.json file can be retrieved via: https://myserver/api/v1/swagger.json.
+The swagger.json file can be retrieved via: https://myserver/api/v1/swagger.json
+(WEB_API=/api)
The swagger.json file can be consumed in programs such as Postman for generating all API calls.
@@ -28,4 +29,5 @@ In-built SwaggerUI
The Mailu API comes with an in-built SwaggerUI. It is a web client that allows
anyone to visualize and interact with the Mailu API.
-It is accessible via the URL: https://myserver/api/v1/swaggerui
+Assuming ``/api`` is configured as value for ``WEB_API``, it
+is accessible via the URL: https://myserver/api/
diff --git a/docs/configuration.rst b/docs/configuration.rst
index b37bec08..51b2554d 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -140,13 +140,15 @@ Web settings
- ``WEB_WEBMAIL`` contains the path to the Web email client.
+- ``WEB_API`` contains the path to the RESTful API.
+
- ``WEBROOT_REDIRECT`` redirects all non-found queries to the set path.
An empty ``WEBROOT_REDIRECT`` value disables redirecting and enables
classic behavior of a 404 result when not found.
Alternatively, ``WEBROOT_REDIRECT`` can be set to ``none`` if you
are using an Nginx override for ``location /``.
-All three options need a leading slash (``/``) to work.
+All four options need a leading slash (``/``) to work.
.. note:: ``WEBROOT_REDIRECT`` has to point to a valid path on the webserver.
This means it cannot point to any services which are not enabled.
@@ -203,13 +205,9 @@ Depending on your particular deployment you most probably will want to change th
Advanced settings
-----------------
-The ``API`` (default: False) setting controls if the API endpoint is reachable.
-
-The ``WEB_API`` (default: /api) setting configures the endpoint that the API
-listens on publicly&interally. The path must always start with a leading slash.
-
-The ``API_TOKEN`` (default: None) enables the API endpoint. This token must be
-passed as request header to the API as authentication token.
+The ``API_TOKEN`` (default: None) configures the authentication token.
+This token must be passed as request header to the API as authentication token.
+This is a mandatory setting for using the RESTful API.
The ``CREDENTIAL_ROUNDS`` (default: 12) setting is the number of rounds used by the
password hashing scheme. The number of rounds can be reduced in case faster
diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env
index 980788ce..4fb25dc0 100644
--- a/setup/flavors/compose/mailu.env
+++ b/setup/flavors/compose/mailu.env
@@ -52,6 +52,9 @@ ADMIN={{ admin_enabled or 'false' }}
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL={{ webmail_type }}
+# Expose the API interface (value: true, false)
+API={{ api_enabled or 'false' }}
+
# Dav server implementation (value: radicale, none)
WEBDAV={{ webdav_enabled or 'none' }}
@@ -131,6 +134,9 @@ WEB_WEBMAIL=/
WEB_WEBMAIL={{ webmail_path }}
{% endif %}
+# Path to the API interface if enabled
+WEB_API={{ api_path }}
+
# Website name
SITENAME={{ site_name }}
@@ -182,6 +188,10 @@ TZ=Etc/UTC
# Default spam threshold used for new users
DEFAULT_SPAM_THRESHOLD=80
+# API token required for authenticating to the RESTful API.
+# This is a mandatory setting for using the RESTful API.
+API_TOKEN={{ api_token }}
+
###################################
# Database settings
###################################
diff --git a/setup/static/render.js b/setup/static/render.js
index f1b8e0a5..2d847a2d 100644
--- a/setup/static/render.js
+++ b/setup/static/render.js
@@ -1,18 +1,18 @@
$(document).ready(function() {
if ($("#webmail").val() == 'none') {
$("#webmail_path").hide();
- $("#webmail_path").attr("value", "");
+ $("#webmail_path").val("");
} else {
$("#webmail_path").show();
- $("#webmail_path").attr("value", "/webmail");
+ $("#webmail_path").val("/webmail");
}
$("#webmail").click(function() {
if (this.value == 'none') {
$("#webmail_path").hide();
- $("#webmail_path").attr("value", "");
+ $("#webmail_path").val("");
} else {
$("#webmail_path").show();
- $("#webmail_path").attr("value", "/webmail");
+ $("#webmail_path").val("/webmail");
}
});
});
@@ -20,15 +20,43 @@ $(document).ready(function() {
$(document).ready(function() {
if ($('#admin').prop('checked')) {
$("#admin_path").show();
- $("#admin_path").attr("value", "/admin");
+ $("#admin_path").val("/admin");
}
$("#admin").change(function() {
if ($(this).is(":checked")) {
$("#admin_path").show();
- $("#admin_path").attr("value", "/admin");
+ $("#admin_path").val("/admin");
} else {
$("#admin_path").hide();
- $("#admin_path").attr("value", "");
+ $("#admin_path").val("");
+ }
+ });
+});
+
+$(document).ready(function() {
+ if ($('#api').prop('checked')) {
+ $("#api_path").show();
+ $("#api_path").val("/api")
+ $("#api_token").show();
+ $("#api_token").prop('required',true);
+ $("#api_token").val("");
+ $("#api_token_label").show();
+ }
+ $("#api").change(function() {
+ if ($(this).is(":checked")) {
+ $("#api_path").show();
+ $("#api_path").val("/api");
+ $("#api_token").show();
+ $("#api_token").prop('required',true);
+ $("#api_token").val("")
+ $("#api_token_label").show();
+ } else {
+ $("#api_path").hide();
+ $("#api_path").val("/api")
+ $("#api_token").hide();
+ $("#api_token").prop('required',false);
+ $("#api_token").val("");
+ $("#api_token_label").hide();
}
});
});
diff --git a/setup/templates/steps/config.html b/setup/templates/steps/config.html
index 5e503230..83a3a813 100644
--- a/setup/templates/steps/config.html
+++ b/setup/templates/steps/config.html
@@ -87,6 +87,19 @@ manage your email domains, users, etc.
+
The API interface is a RESTful API for changing the Mailu configuration.
+ Anything that can be configured via the Mailu web administration interface,
+ can also be configured via the RESTful API. For enabling the API, an API token must be configured.
+ It is not possible to use the API without an API token.
+
+
+
+
+
+
+
+
+
From 18b900699cfa06bd9ac5a9aebe615de5877c250a Mon Sep 17 00:00:00 2001
From: Dimitri Huisman
Date: Wed, 25 Jan 2023 16:12:14 +0000
Subject: [PATCH 2/3] Bump version of Flask-RESTX to 1.0.5. This resolves all
deprecation warnings caused by Flask-RESTX.
---
core/base/requirements-prod.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/base/requirements-prod.txt b/core/base/requirements-prod.txt
index e9f664b4..5119520e 100644
--- a/core/base/requirements-prod.txt
+++ b/core/base/requirements-prod.txt
@@ -27,7 +27,7 @@ Flask-DebugToolbar==0.13.1
Flask-Login==0.6.2
flask-marshmallow==0.14.0
Flask-Migrate==3.1.0
-Flask-RESTX==1.0.3
+Flask-RESTX==1.0.5
Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.1
frozenlist==1.3.1
From 8cb7265eb2f7d8f2837801a717c0b36b7bf2b2c0 Mon Sep 17 00:00:00 2001
From: Dimitri Huisman
Date: Fri, 27 Jan 2023 13:17:36 +0000
Subject: [PATCH 3/3] By default disable the API in the setup utility. Generate
a sample token value for API_TOKEN. Fix small rendering issue when API was
disabled in setup.
---
setup/static/render.js | 21 +++++++++++++++++++--
setup/templates/steps/config.html | 2 +-
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/setup/static/render.js b/setup/static/render.js
index 2d847a2d..b2cdc7c8 100644
--- a/setup/static/render.js
+++ b/setup/static/render.js
@@ -1,3 +1,13 @@
+//API_TOKEN generator
+var chars = "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+var tokenLength = 12;
+var token = "";
+
+for (var i = 0; i <= tokenLength; i++) {
+ var randomNumber = Math.floor(Math.random() * chars.length);
+ token += chars.substring(randomNumber, randomNumber +1);
+ }
+
$(document).ready(function() {
if ($("#webmail").val() == 'none') {
$("#webmail_path").hide();
@@ -39,8 +49,15 @@ $(document).ready(function() {
$("#api_path").val("/api")
$("#api_token").show();
$("#api_token").prop('required',true);
- $("#api_token").val("");
+ $("#api_token").val(token);
$("#api_token_label").show();
+ } else {
+ $("#api_path").hide();
+ $("#api_path").val("/api")
+ $("#api_token").hide();
+ $("#api_token").prop('required',false);
+ $("#api_token").val("");
+ $("#api_token_label").hide();
}
$("#api").change(function() {
if ($(this).is(":checked")) {
@@ -48,7 +65,7 @@ $(document).ready(function() {
$("#api_path").val("/api");
$("#api_token").show();
$("#api_token").prop('required',true);
- $("#api_token").val("")
+ $("#api_token").val(token)
$("#api_token_label").show();
} else {
$("#api_path").hide();
diff --git a/setup/templates/steps/config.html b/setup/templates/steps/config.html
index 83a3a813..19736448 100644
--- a/setup/templates/steps/config.html
+++ b/setup/templates/steps/config.html
@@ -93,7 +93,7 @@ manage your email domains, users, etc.
It is not possible to use the API without an API token.