mirror of
https://github.com/Mailu/Mailu.git
synced 2025-02-07 13:08:22 +02:00
Add automatic tests for RESTful API. Fix all remaining issues that I could find with the API.
This commit is contained in:
parent
c8e3270724
commit
2558ae3bc9
2
.github/workflows/build_test_deploy.yml
vendored
2
.github/workflows/build_test_deploy.yml
vendored
@ -418,7 +418,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: ["core", "fetchmail", "filters", "webmail", "webdav"]
|
||||
target: ["api", "core", "fetchmail", "filters", "webmail", "webdav"]
|
||||
time: ["2"]
|
||||
include:
|
||||
- target: "filters"
|
||||
|
@ -2,6 +2,7 @@ from flask_restx import Resource, fields, marshal
|
||||
from . import api, response_fields
|
||||
from .. import common
|
||||
from ... import models
|
||||
import validators
|
||||
|
||||
db = models.db
|
||||
|
||||
@ -15,7 +16,7 @@ alias_fields_update = alias.model('AliasUpdate', {
|
||||
|
||||
alias_fields = alias.inherit('Alias',alias_fields_update, {
|
||||
'email': fields.String(description='the alias email address', example='user@example.com', required=True),
|
||||
'destination': fields.List(fields.String(description='alias email address', example='user@example.com', required=True)),
|
||||
'destination': fields.List(fields.String(description='destination email address', example='user@example.com', required=True)),
|
||||
|
||||
})
|
||||
|
||||
@ -24,6 +25,7 @@ alias_fields = alias.inherit('Alias',alias_fields_update, {
|
||||
class Aliases(Resource):
|
||||
@alias.doc('list_alias')
|
||||
@alias.marshal_with(alias_fields, as_list=True, skip_none=True, mask=None)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -34,6 +36,8 @@ class Aliases(Resource):
|
||||
@alias.expect(alias_fields)
|
||||
@alias.response(200, 'Success', response_fields)
|
||||
@alias.response(400, 'Input validation exception', response_fields)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.response(404, 'Not found', response_fields)
|
||||
@alias.response(409, 'Duplicate alias', response_fields)
|
||||
@alias.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -41,6 +45,20 @@ class Aliases(Resource):
|
||||
""" Create a new alias """
|
||||
data = api.payload
|
||||
|
||||
if not validators.email(data['email']):
|
||||
return { 'code': 400, 'message': f'Provided alias {data["email"]} is not a valid email address'}, 400
|
||||
localpart, domain_name = data['email'].lower().rsplit('@', 1)
|
||||
domain_found = models.Domain.query.get(domain_name)
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain_name} does not exist ({data["email"]})'}, 404
|
||||
if not domain_found.max_aliases == -1 and len(domain_found.aliases) >= domain_found.max_aliases:
|
||||
return { 'code': 409, 'message': f'Too many aliases for domain {domain_name}'}, 409
|
||||
for dest in data['destination']:
|
||||
if not validators.email(dest):
|
||||
return { 'code': 400, 'message': f'Provided destination email address {dest} is not a valid email address'}, 400
|
||||
elif models.User.query.filter_by(email=dest).first() is None:
|
||||
return { 'code': 404, 'message': f'Provided destination email address {dest} does not exist'}, 404
|
||||
|
||||
alias_found = models.Alias.query.filter_by(email = data['email']).first()
|
||||
if alias_found:
|
||||
return { 'code': 409, 'message': f'Duplicate alias {data["email"]}'}, 409
|
||||
@ -53,17 +71,21 @@ class Aliases(Resource):
|
||||
db.session.add(alias_model)
|
||||
db.session.commit()
|
||||
|
||||
return {'code': 200, 'message': f'Alias {data["email"]} to destination {data["destination"]} has been created'}, 200
|
||||
return {'code': 200, 'message': f'Alias {data["email"]} to destination(s) {data["destination"]} has been created'}, 200
|
||||
|
||||
@alias.route('/<string:alias>')
|
||||
class Alias(Resource):
|
||||
@alias.doc('find_alias')
|
||||
@alias.response(200, 'Success', alias_fields)
|
||||
@alias.response(400, 'Input validation exception', response_fields)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.response(404, 'Alias not found', response_fields)
|
||||
@alias.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self, alias):
|
||||
""" Look up the specified alias """
|
||||
if not validators.email(alias):
|
||||
return { 'code': 400, 'message': f'Provided alias (email address) {alias} is not a valid email address'}, 400
|
||||
alias_found = models.Alias.query.filter_by(email = alias).first()
|
||||
if alias_found is None:
|
||||
return { 'code': 404, 'message': f'Alias {alias} cannot be found'}, 404
|
||||
@ -73,6 +95,7 @@ class Alias(Resource):
|
||||
@alias.doc('update_alias')
|
||||
@alias.expect(alias_fields_update)
|
||||
@alias.response(200, 'Success', response_fields)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.response(404, 'Alias not found', response_fields)
|
||||
@alias.response(400, 'Input validation exception', response_fields)
|
||||
@alias.doc(security='Bearer')
|
||||
@ -80,6 +103,9 @@ class Alias(Resource):
|
||||
def patch(self, alias):
|
||||
""" Update the specfied alias """
|
||||
data = api.payload
|
||||
|
||||
if not validators.email(alias):
|
||||
return { 'code': 400, 'message': f'Provided alias (email address) {alias} is not a valid email address'}, 400
|
||||
alias_found = models.Alias.query.filter_by(email = alias).first()
|
||||
if alias_found is None:
|
||||
return { 'code': 404, 'message': f'Alias {alias} cannot be found'}, 404
|
||||
@ -87,6 +113,11 @@ class Alias(Resource):
|
||||
alias_found.comment = data['comment']
|
||||
if 'destination' in data:
|
||||
alias_found.destination = data['destination']
|
||||
for dest in data['destination']:
|
||||
if not validators.email(dest):
|
||||
return { 'code': 400, 'message': f'Provided destination email address {dest} is not a valid email address'}, 400
|
||||
elif models.User.query.filter_by(email=dest).first() is None:
|
||||
return { 'code': 404, 'message': f'Provided destination email address {dest} does not exist'}, 404
|
||||
if 'wildcard' in data:
|
||||
alias_found.wildcard = data['wildcard']
|
||||
db.session.add(alias_found)
|
||||
@ -95,11 +126,15 @@ class Alias(Resource):
|
||||
|
||||
@alias.doc('delete_alias')
|
||||
@alias.response(200, 'Success', response_fields)
|
||||
@alias.response(400, 'Input validation exception', response_fields)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.response(404, 'Alias not found', response_fields)
|
||||
@alias.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def delete(self, alias):
|
||||
""" Delete the specified alias """
|
||||
if not validators.email(alias):
|
||||
return { 'code': 400, 'message': f'Provided alias (email address) {alias} is not a valid email address'}, 400
|
||||
alias_found = models.Alias.query.filter_by(email = alias).first()
|
||||
if alias_found is None:
|
||||
return { 'code': 404, 'message': f'Alias {alias} cannot be found'}, 404
|
||||
@ -110,12 +145,16 @@ class Alias(Resource):
|
||||
@alias.route('/destination/<string:domain>')
|
||||
class AliasWithDest(Resource):
|
||||
@alias.doc('find_alias_filter_domain')
|
||||
@alias.marshal_with(alias_fields, code=200, description='Success' ,as_list=True, skip_none=True, mask=None)
|
||||
@alias.response(200, 'Success', alias_fields)
|
||||
@alias.response(400, 'Input validation exception', response_fields)
|
||||
@alias.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alias.response(404, 'Alias or domain not found', response_fields)
|
||||
@alias.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self, domain):
|
||||
""" Look up the aliases of the specified domain """
|
||||
if not validators.domain(domain):
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 400
|
||||
domain_found = models.Domain.query.filter_by(name=domain).first()
|
||||
if domain_found is None:
|
||||
return { 'code': 404, 'message': f'Domain {domain} cannot be found'}, 404
|
||||
@ -123,4 +162,4 @@ class AliasWithDest(Resource):
|
||||
if aliases_found.count == 0:
|
||||
return { 'code': 404, 'message': f'No alias can be found for domain {domain}'}, 404
|
||||
else:
|
||||
return marshal(aliases_found, alias_fields, as_list=True), 200
|
||||
return marshal(aliases_found, alias_fields), 200
|
||||
|
@ -16,7 +16,7 @@ domain_fields = api.model('Domain', {
|
||||
'max_aliases': fields.Integer(description='maximum number of aliases', min=-1, default=-1),
|
||||
'max_quota_bytes': fields.Integer(description='maximum quota for mailbox', min=0),
|
||||
'signup_enabled': fields.Boolean(description='allow signup'),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN', example='example2.com')),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN'), example='["example.com"]'),
|
||||
})
|
||||
|
||||
domain_fields_update = api.model('DomainUpdate', {
|
||||
@ -25,17 +25,18 @@ domain_fields_update = api.model('DomainUpdate', {
|
||||
'max_aliases': fields.Integer(description='maximum number of aliases', min=-1, default=-1),
|
||||
'max_quota_bytes': fields.Integer(description='maximum quota for mailbox', min=0),
|
||||
'signup_enabled': fields.Boolean(description='allow signup'),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN', example='example2.com')),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN'), example='["example.com"]'),
|
||||
})
|
||||
|
||||
domain_fields_get = api.model('DomainGet', {
|
||||
'name': fields.String(description='FQDN (e.g. example.com)', example='example.com', required=True),
|
||||
'comment': fields.String(description='a comment'),
|
||||
'managers': fields.List(fields.String(attribute='email', description='manager of domain')),
|
||||
'max_users': fields.Integer(description='maximum number of users', min=-1, default=-1),
|
||||
'max_aliases': fields.Integer(description='maximum number of aliases', min=-1, default=-1),
|
||||
'max_quota_bytes': fields.Integer(description='maximum quota for mailbox', min=0),
|
||||
'signup_enabled': fields.Boolean(description='allow signup'),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN', example='example2.com')),
|
||||
'alternatives': fields.List(fields.String(attribute='name', description='FQDN'), example='["example.com"]'),
|
||||
'dns_autoconfig': fields.List(fields.String(description='DNS client auto-configuration entry')),
|
||||
'dns_mx': fields.String(Description='MX record for domain'),
|
||||
'dns_spf': fields.String(Description='SPF record for domain'),
|
||||
@ -56,8 +57,7 @@ domain_fields_dns = api.model('DomainDNS', {
|
||||
})
|
||||
|
||||
manager_fields = api.model('Manager', {
|
||||
'domain_name': fields.String(description='domain managed by manager'),
|
||||
'user_email': fields.String(description='email address of manager'),
|
||||
'managers': fields.List(fields.String(attribute='email', description='manager of domain')),
|
||||
})
|
||||
|
||||
manager_fields_create = api.model('ManagerCreate', {
|
||||
@ -78,6 +78,7 @@ alternative_fields = api.model('AlternativeDomain', {
|
||||
class Domains(Resource):
|
||||
@dom.doc('list_domain')
|
||||
@dom.marshal_with(domain_fields_get, as_list=True, skip_none=True, mask=None)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -88,6 +89,7 @@ class Domains(Resource):
|
||||
@dom.expect(domain_fields)
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(409, 'Duplicate domain/alternative name', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -131,15 +133,16 @@ class Domains(Resource):
|
||||
class Domain(Resource):
|
||||
|
||||
@dom.doc('find_domain')
|
||||
@dom.marshal_with(domain_fields, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@dom.response(200, 'Success', domain_fields_get)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Domain not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self, domain):
|
||||
""" Look up the specified domain """
|
||||
if not validators.domain(domain):
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 400
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 200
|
||||
domain_found = models.Domain.query.get(domain)
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain} does not exist'}, 404
|
||||
@ -149,6 +152,7 @@ class Domain(Resource):
|
||||
@dom.expect(domain_fields_update)
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Domain not found', response_fields)
|
||||
@dom.response(409, 'Duplicate domain/alternative name', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@ -158,7 +162,7 @@ class Domain(Resource):
|
||||
if not validators.domain(domain):
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 400
|
||||
domain_found = models.Domain.query.get(domain)
|
||||
if not domain:
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {data["name"]} does not exist'}, 404
|
||||
data = api.payload
|
||||
|
||||
@ -172,7 +176,7 @@ class Domain(Resource):
|
||||
if not validators.domain(item):
|
||||
return { 'code': 400, 'message': f'Alternative domain {item} is not a valid domain'}, 400
|
||||
for item in data['alternatives']:
|
||||
alternative = models.Alternative(name=item, domain_name=data['name'])
|
||||
alternative = models.Alternative(name=item, domain_name=domain)
|
||||
models.db.session.add(alternative)
|
||||
|
||||
if 'comment' in data:
|
||||
@ -194,6 +198,7 @@ class Domain(Resource):
|
||||
@dom.doc('delete_domain')
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Domain not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -202,7 +207,7 @@ class Domain(Resource):
|
||||
if not validators.domain(domain):
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 400
|
||||
domain_found = models.Domain.query.get(domain)
|
||||
if not domain:
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain} does not exist'}, 404
|
||||
db.session.delete(domain_found)
|
||||
db.session.commit()
|
||||
@ -213,6 +218,7 @@ class Domain(Resource):
|
||||
@dom.doc('generate_dkim')
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Domain not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -230,8 +236,9 @@ class Domain(Resource):
|
||||
@dom.route('/<domain>/manager')
|
||||
class Manager(Resource):
|
||||
@dom.doc('list_managers')
|
||||
@dom.marshal_with(manager_fields, code=200, description='Success', as_list=True, skip_none=True, mask=None)
|
||||
@dom.response(200, 'Success', manager_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'domain not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -239,15 +246,16 @@ class Manager(Resource):
|
||||
""" List all managers of the specified domain """
|
||||
if not validators.domain(domain):
|
||||
return { 'code': 400, 'message': f'Domain {domain} is not a valid domain'}, 400
|
||||
if not domain:
|
||||
domain_found = models.Domain.query.get(domain)
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain} does not exist'}, 404
|
||||
domain = models.Domain.query.filter_by(name=domain)
|
||||
return domain.managers
|
||||
return marshal(domain_found, manager_fields), 200
|
||||
|
||||
@dom.doc('create_manager')
|
||||
@dom.expect(manager_fields_create)
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'User or domain not found', response_fields)
|
||||
@dom.response(409, 'Duplicate domain manager', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@ -274,13 +282,14 @@ class Manager(Resource):
|
||||
@dom.route('/<domain>/manager/<email>')
|
||||
class Domain(Resource):
|
||||
@dom.doc('find_manager')
|
||||
@dom.marshal_with(manager_fields, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Manager not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self, domain, email):
|
||||
""" Look up the specified manager of the specified domain """
|
||||
""" Check if the specified user is a manager of the specified domain """
|
||||
if not validators.email(email):
|
||||
return {'code': 400, 'message': f'Invalid email address {email}'}, 400
|
||||
if not validators.domain(domain):
|
||||
@ -294,7 +303,7 @@ class Domain(Resource):
|
||||
if user in domain.managers:
|
||||
for manager in domain.managers:
|
||||
if manager.email == email:
|
||||
return marshal(manager, manager_fields),200
|
||||
return { 'code': 200, 'message': f'User {email} is a manager of the domain {domain}'}, 200
|
||||
else:
|
||||
return { 'code': 404, 'message': f'User {email} is not a manager of the domain {domain}'}, 404
|
||||
|
||||
@ -302,6 +311,7 @@ class Domain(Resource):
|
||||
@dom.doc('delete_manager')
|
||||
@dom.response(200, 'Success', response_fields)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Manager not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -327,8 +337,9 @@ class Domain(Resource):
|
||||
@dom.route('/<domain>/users')
|
||||
class User(Resource):
|
||||
@dom.doc('list_user_domain')
|
||||
@dom.marshal_with(user.user_fields_get, code=200, description='Success', as_list=True, skip_none=True, mask=None)
|
||||
@dom.response(200, 'Success', user.user_fields_get)
|
||||
@dom.response(400, 'Input validation exception', response_fields)
|
||||
@dom.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@dom.response(404, 'Domain not found', response_fields)
|
||||
@dom.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -339,13 +350,14 @@ class User(Resource):
|
||||
domain_found = models.Domain.query.get(domain)
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain} does not exist'}, 404
|
||||
return models.User.query.filter_by(domain=domain_found).all()
|
||||
return marshal(models.User.query.filter_by(domain=domain_found).all(), user.user_fields_get),200
|
||||
|
||||
@alt.route('')
|
||||
class Alternatives(Resource):
|
||||
|
||||
@alt.doc('list_alternative')
|
||||
@alt.marshal_with(alternative_fields, as_list=True, skip_none=True, mask=None)
|
||||
@alt.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alt.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -357,6 +369,7 @@ class Alternatives(Resource):
|
||||
@alt.expect(alternative_fields)
|
||||
@alt.response(200, 'Success', response_fields)
|
||||
@alt.response(400, 'Input validation exception', response_fields)
|
||||
@alt.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alt.response(404, 'Domain not found or missing', response_fields)
|
||||
@alt.response(409, 'Duplicate alternative domain name', response_fields)
|
||||
@alt.doc(security='Bearer')
|
||||
@ -383,8 +396,9 @@ class Alternatives(Resource):
|
||||
class Alternative(Resource):
|
||||
@alt.doc('find_alternative')
|
||||
@alt.doc(security='Bearer')
|
||||
@alt.marshal_with(alternative_fields, code=200, description='Success' ,as_list=True, skip_none=True, mask=None)
|
||||
@alt.response(200, 'Success', alternative_fields)
|
||||
@alt.response(400, 'Input validation exception', response_fields)
|
||||
@alt.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alt.response(404, 'Alternative not found or missing', response_fields)
|
||||
@common.api_token_authorization
|
||||
def get(self, alt):
|
||||
@ -399,6 +413,7 @@ class Alternative(Resource):
|
||||
@alt.doc('delete_alternative')
|
||||
@alt.response(200, 'Success', response_fields)
|
||||
@alt.response(400, 'Input validation exception', response_fields)
|
||||
@alt.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@alt.response(404, 'Alternative/Domain not found or missing', response_fields)
|
||||
@alt.response(409, 'Duplicate domain name', response_fields)
|
||||
@alt.doc(security='Bearer')
|
||||
|
@ -24,6 +24,7 @@ relay_fields_update = api.model('RelayUpdate', {
|
||||
class Relays(Resource):
|
||||
@relay.doc('list_relays')
|
||||
@relay.marshal_with(relay_fields, as_list=True, skip_none=True, mask=None)
|
||||
@relay.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@relay.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -34,6 +35,7 @@ class Relays(Resource):
|
||||
@relay.expect(relay_fields)
|
||||
@relay.response(200, 'Success', response_fields)
|
||||
@relay.response(400, 'Input validation exception', response_fields)
|
||||
@relay.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@relay.response(409, 'Duplicate relay', response_fields)
|
||||
@relay.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -58,8 +60,9 @@ class Relays(Resource):
|
||||
@relay.route('/<string:name>')
|
||||
class Relay(Resource):
|
||||
@relay.doc('find_relay')
|
||||
@relay.marshal_with(relay_fields, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@relay.response(200, 'Success', relay_fields)
|
||||
@relay.response(400, 'Input validation exception', response_fields)
|
||||
@relay.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@relay.response(404, 'Relay not found', response_fields)
|
||||
@relay.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -77,6 +80,7 @@ class Relay(Resource):
|
||||
@relay.expect(relay_fields_update)
|
||||
@relay.response(200, 'Success', response_fields)
|
||||
@relay.response(400, 'Input validation exception', response_fields)
|
||||
@relay.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@relay.response(404, 'Relay not found', response_fields)
|
||||
@relay.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -103,6 +107,7 @@ class Relay(Resource):
|
||||
@relay.doc('delete_relay')
|
||||
@relay.response(200, 'Success', response_fields)
|
||||
@relay.response(400, 'Input validation exception', response_fields)
|
||||
@relay.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@relay.response(404, 'Relay not found', response_fields)
|
||||
@relay.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
|
@ -15,20 +15,20 @@ token_user_fields = api.model('TokenGetResponse', {
|
||||
'id': fields.String(description='The record id of the token (unique identifier)', example='1'),
|
||||
'email': fields.String(description='The email address of the user', example='John.Doe@example.com', attribute='user_email'),
|
||||
'comment': fields.String(description='A description for the token. This description is shown on the Authentication tokens page', example='my comment'),
|
||||
'AuthorizedIP': fields.String(description='Comma separated list of white listed IP addresses or networks that may use this token.', example="['203.0.113.0/24']", attribute='ip'),
|
||||
'AuthorizedIP': fields.List(fields.String(description='White listed IP addresses or networks that may use this token.', example="203.0.113.0/24"), attribute='ip'),
|
||||
'Created': fields.String(description='The date when the token was created', example='John.Doe@example.com', attribute='created_at'),
|
||||
'Last edit': fields.String(description='The date when the token was last modifified', example='John.Doe@example.com', attribute='updated_at')
|
||||
})
|
||||
|
||||
token_user_fields_post = api.model('TokenPost', {
|
||||
'email': fields.String(description='The email address of the user', example='John.Doe@example.com', attribute='user_email'),
|
||||
'email': fields.String(description='The email address of the user', example='John.Doe@example.com', attribute='user_email', required=True),
|
||||
'comment': fields.String(description='A description for the token. This description is shown on the Authentication tokens page', example='my comment'),
|
||||
'AuthorizedIP': fields.String(description='Comma separated list of white listed IP addresses or networks that may use this token.', example="['203.0.113.0/24']", attribute='ip'),
|
||||
'AuthorizedIP': fields.List(fields.String(description='White listed IP addresses or networks that may use this token.', example="203.0.113.0/24")),
|
||||
})
|
||||
|
||||
token_user_fields_post2 = api.model('TokenPost2', {
|
||||
'comment': fields.String(description='A description for the token. This description is shown on the Authentication tokens page', example='my comment'),
|
||||
'AuthorizedIP': fields.String(description='Comma separated list of white listed IP addresses or networks that may use this token.', example="['203.0.113.0/24']", attribute='ip'),
|
||||
'AuthorizedIP': fields.List(fields.String(description='White listed IP addresses or networks that may use this token.', example="203.0.113.0/24")),
|
||||
})
|
||||
|
||||
token_user_post_response = api.model('TokenPostResponse', {
|
||||
@ -36,14 +36,17 @@ token_user_post_response = api.model('TokenPostResponse', {
|
||||
'token': fields.String(description='The created authentication token for the user.', example='2caf6607de5129e4748a2c061aee56f2', attribute='password'),
|
||||
'email': fields.String(description='The email address of the user', example='John.Doe@example.com', attribute='user_email'),
|
||||
'comment': fields.String(description='A description for the token. This description is shown on the Authentication tokens page', example='my comment'),
|
||||
'AuthorizedIP': fields.String(description='Comma separated list of white listed IP addresses or networks that may use this token.', example="['203.0.113.0/24']", attribute='ip'),
|
||||
'AuthorizedIP': fields.List(fields.String(description='White listed IP addresses or networks that may use this token.', example="203.0.113.0/24")),
|
||||
'Created': fields.String(description='The date when the token was created', example='John.Doe@example.com', attribute='created_at')
|
||||
})
|
||||
|
||||
|
||||
|
||||
@token.route('')
|
||||
class Tokens(Resource):
|
||||
@token.doc('list_tokens')
|
||||
@token.marshal_with(token_user_fields, as_list=True, skip_none=True, mask=None)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -52,8 +55,10 @@ class Tokens(Resource):
|
||||
|
||||
@token.doc('create_token')
|
||||
@token.expect(token_user_fields_post)
|
||||
@token.marshal_with(token_user_post_response, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@token.response(200, 'Success', token_user_post_response)
|
||||
@token.response(400, 'Input validation exception', response_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'User not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def post(self):
|
||||
@ -71,7 +76,11 @@ class Tokens(Resource):
|
||||
if 'comment' in data:
|
||||
token_new.comment = data['comment']
|
||||
if 'AuthorizedIP' in data:
|
||||
token_new.ip = data['AuthorizedIP'].replace(' ','').split(',')
|
||||
token_new.ip = data['AuthorizedIP']
|
||||
for ip in token_new.ip:
|
||||
if (not validators.ip_address.ipv4(ip,cidr=True, strict=False, host_bit=False) and
|
||||
not validators.ip_address.ipv6(ip,cidr=True, strict=False, host_bit=False)):
|
||||
return { 'code': 400, 'message': f'Provided AuthorizedIP {ip} in {token_new.ip} is invalid'}, 400
|
||||
raw_password = pwd.genword(entropy=128, length=32, charset="hex")
|
||||
token_new.set_password(raw_password)
|
||||
models.db.session.add(token_new)
|
||||
@ -91,8 +100,9 @@ class Tokens(Resource):
|
||||
@token.route('user/<string:email>')
|
||||
class Token(Resource):
|
||||
@token.doc('find_tokens_of_user')
|
||||
@token.marshal_with(token_user_fields, code=200, description='Success', as_list=True, skip_none=True, mask=None)
|
||||
@token.response(200, 'Success', token_user_fields)
|
||||
@token.response(400, 'Input validation exception', response_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'Token not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -104,12 +114,25 @@ class Token(Resource):
|
||||
if not user_found:
|
||||
return {'code': 404, 'message': f'User {email} cannot be found'}, 404
|
||||
tokens = user_found.tokens
|
||||
return tokens
|
||||
response_list = []
|
||||
for token in tokens:
|
||||
response_dict = {
|
||||
'id' : token.id,
|
||||
'email' : token.user_email,
|
||||
'comment' : token.comment,
|
||||
'AuthorizedIP' : token.ip,
|
||||
'Created': str(token.created_at),
|
||||
'Last edit': str(token.updated_at)
|
||||
}
|
||||
response_list.append(response_dict)
|
||||
return response_list
|
||||
|
||||
@token.doc('create_token')
|
||||
@token.expect(token_user_fields_post2)
|
||||
@token.response(200, 'Success', token_user_post_response)
|
||||
@token.response(400, 'Input validation exception', response_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'User not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def post(self, email):
|
||||
@ -125,7 +148,11 @@ class Token(Resource):
|
||||
if 'comment' in data:
|
||||
token_new.comment = data['comment']
|
||||
if 'AuthorizedIP' in data:
|
||||
token_new.ip = token_new.ip = data['AuthorizedIP'].replace(' ','').split(',')
|
||||
token_new.ip = token_new.ip = data['AuthorizedIP']
|
||||
for ip in token_new.ip:
|
||||
if (not validators.ip_address.ipv4(ip,cidr=True, strict=False, host_bit=False) and
|
||||
not validators.ip_address.ipv6(ip,cidr=True, strict=False, host_bit=False)):
|
||||
return { 'code': 400, 'message': f'Provided AuthorizedIP {ip} in {token_new.ip} is invalid'}, 400
|
||||
raw_password = pwd.genword(entropy=128, length=32, charset="hex")
|
||||
token_new.set_password(raw_password)
|
||||
models.db.session.add(token_new)
|
||||
@ -144,7 +171,8 @@ class Token(Resource):
|
||||
@token.route('/<string:token_id>')
|
||||
class Token(Resource):
|
||||
@token.doc('find_token')
|
||||
@token.marshal_with(token_user_fields, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@token.response(200, 'Success', token_user_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'Token not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -153,11 +181,48 @@ class Token(Resource):
|
||||
token = models.Token.query.get(token_id)
|
||||
if not token:
|
||||
return { 'code' : 404, 'message' : f'Record cannot be found for id {token_id} or invalid id provided'}, 404
|
||||
return token
|
||||
response_dict = {
|
||||
'id' : token.id,
|
||||
'email' : token.user_email,
|
||||
'comment' : token.comment,
|
||||
'AuthorizedIP' : token.ip,
|
||||
'Created': str(token.created_at),
|
||||
'Last edit': str(token.updated_at)
|
||||
}
|
||||
return response_dict
|
||||
|
||||
@token.doc('update_token')
|
||||
@token.expect(token_user_fields_post2)
|
||||
@token.response(200, 'Success', response_fields)
|
||||
@token.response(400, 'Input validation exception', response_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'User not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
def patch(self, token_id):
|
||||
""" Update the specified token """
|
||||
data = api.payload
|
||||
|
||||
token = models.Token.query.get(token_id)
|
||||
if not token:
|
||||
return { 'code' : 404, 'message' : f'Record cannot be found for id {token_id} or invalid id provided'}, 404
|
||||
|
||||
if 'comment' in data:
|
||||
token.comment = data['comment']
|
||||
if 'AuthorizedIP' in data:
|
||||
token.ip = token.ip = data['AuthorizedIP']
|
||||
for ip in token.ip:
|
||||
if (not validators.ip_address.ipv4(ip,cidr=True, strict=False, host_bit=False) and
|
||||
not validators.ip_address.ipv6(ip,cidr=True, strict=False, host_bit=False)):
|
||||
return { 'code': 400, 'message': f'Provided AuthorizedIP {ip} in {token.ip} is invalid'}, 400
|
||||
models.db.session.add(token)
|
||||
#apply the changes
|
||||
db.session.commit()
|
||||
return {'code': 200, 'message': f'Token with id {token_id} has been updated'}, 200
|
||||
|
||||
|
||||
@token.doc('delete_token')
|
||||
@token.response(200, 'Success', response_fields)
|
||||
@token.response(400, 'Input validation exception', response_fields)
|
||||
@token.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@token.response(404, 'Token not found', response_fields)
|
||||
@token.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
|
@ -22,7 +22,7 @@ user_fields_get = api.model('UserGet', {
|
||||
'enable_pop': fields.Boolean(description='Allow email retrieval via POP3'),
|
||||
'allow_spoofing': fields.Boolean(description='Allow the user to spoof the sender (send email as anyone)'),
|
||||
'forward_enabled': fields.Boolean(description='Enable auto forwarding'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='Other@example.com'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='["Other@example.com"]'),
|
||||
'forward_keep': fields.Boolean(description='Keep a copy of the forwarded email in the inbox'),
|
||||
'reply_enabled': fields.Boolean(description='Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies'),
|
||||
'reply_subject': fields.String(description='Optional subject for the automatic reply', example='Out of office'),
|
||||
@ -47,7 +47,7 @@ user_fields_post = api.model('UserCreate', {
|
||||
'enable_pop': fields.Boolean(description='Allow email retrieval via POP3'),
|
||||
'allow_spoofing': fields.Boolean(description='Allow the user to spoof the sender (send email as anyone)'),
|
||||
'forward_enabled': fields.Boolean(description='Enable auto forwarding'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='Other@example.com'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='["Other@example.com"]'),
|
||||
'forward_keep': fields.Boolean(description='Keep a copy of the forwarded email in the inbox'),
|
||||
'reply_enabled': fields.Boolean(description='Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies'),
|
||||
'reply_subject': fields.String(description='Optional subject for the automatic reply', example='Out of office'),
|
||||
@ -71,7 +71,7 @@ user_fields_put = api.model('UserUpdate', {
|
||||
'enable_pop': fields.Boolean(description='Allow email retrieval via POP3'),
|
||||
'allow_spoofing': fields.Boolean(description='Allow the user to spoof the sender (send email as anyone)'),
|
||||
'forward_enabled': fields.Boolean(description='Enable auto forwarding'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='Other@example.com'),
|
||||
'forward_destination': fields.List(fields.String(description='Email address to forward emails to'), example='["Other@example.com"]'),
|
||||
'forward_keep': fields.Boolean(description='Keep a copy of the forwarded email in the inbox'),
|
||||
'reply_enabled': fields.Boolean(description='Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies'),
|
||||
'reply_subject': fields.String(description='Optional subject for the automatic reply', example='Out of office'),
|
||||
@ -89,6 +89,7 @@ user_fields_put = api.model('UserUpdate', {
|
||||
class Users(Resource):
|
||||
@user.doc('list_user')
|
||||
@user.marshal_with(user_fields_get, as_list=True, skip_none=True, mask=None)
|
||||
@user.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@user.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
def get(self):
|
||||
@ -99,6 +100,7 @@ class Users(Resource):
|
||||
@user.expect(user_fields_post)
|
||||
@user.response(200, 'Success', response_fields)
|
||||
@user.response(400, 'Input validation exception', response_fields)
|
||||
@user.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@user.response(409, 'Duplicate user', response_fields)
|
||||
@user.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -111,11 +113,12 @@ class Users(Resource):
|
||||
domain_found = models.Domain.query.get(domain_name)
|
||||
if not domain_found:
|
||||
return { 'code': 404, 'message': f'Domain {domain_name} does not exist'}, 404
|
||||
if not domain_found.max_users == -1 and len(domain_found.users) >= domain_found.max_users:
|
||||
return { 'code': 409, 'message': f'Too many users for domain {domain_name}'}, 409
|
||||
email_found = models.User.query.filter_by(email=data['email']).first()
|
||||
if email_found:
|
||||
return { 'code': 409, 'message': f'User {data["email"]} already exists'}, 409
|
||||
|
||||
|
||||
user_new = models.User(email=data['email'])
|
||||
if 'raw_password' in data:
|
||||
user_new.set_password(data['raw_password'])
|
||||
@ -168,12 +171,12 @@ class Users(Resource):
|
||||
|
||||
return {'code': 200,'message': f'User {data["email"]} has been created'}, 200
|
||||
|
||||
|
||||
@user.route('/<string:email>')
|
||||
class User(Resource):
|
||||
@user.doc('find_user')
|
||||
@user.marshal_with(user_fields_get, code=200, description='Success', as_list=False, skip_none=True, mask=None)
|
||||
@user.response(200, 'Success', user_fields_get)
|
||||
@user.response(400, 'Input validation exception', response_fields)
|
||||
@user.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@user.response(404, 'User not found', response_fields)
|
||||
@user.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -191,6 +194,7 @@ class User(Resource):
|
||||
@user.expect(user_fields_put)
|
||||
@user.response(200, 'Success', response_fields)
|
||||
@user.response(400, 'Input validation exception', response_fields)
|
||||
@user.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@user.response(404, 'User not found', response_fields)
|
||||
@user.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
@ -198,7 +202,7 @@ class User(Resource):
|
||||
""" Update the specified user """
|
||||
data = api.payload
|
||||
if not validators.email(email):
|
||||
return { 'code': 400, 'message': f'Provided email address {data["email"]} is not a valid email address'}, 400
|
||||
return { 'code': 400, 'message': f'Provided email address {email} is not a valid email address'}, 400
|
||||
user_found = models.User.query.get(email)
|
||||
if not user_found:
|
||||
return {'code': 404, 'message': f'User {email} cannot be found'}, 404
|
||||
@ -258,6 +262,7 @@ class User(Resource):
|
||||
@user.doc('delete_user')
|
||||
@user.response(200, 'Success', response_fields)
|
||||
@user.response(400, 'Input validation exception', response_fields)
|
||||
@user.doc(responses={401: 'Authorization header missing', 403: 'Invalid authorization header'})
|
||||
@user.response(404, 'User not found', response_fields)
|
||||
@user.doc(security='Bearer')
|
||||
@common.api_token_authorization
|
||||
|
86
tests/compose/api/00_create_users.sh
Executable file
86
tests/compose/api/00_create_users.sh
Executable file
@ -0,0 +1,86 @@
|
||||
# create user admin@maiu.io
|
||||
echo "Create users"
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://localhost/api/v1/user' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"email": "admin@mailu.io",
|
||||
"raw_password": "password",
|
||||
"comment": "created for testing RESTful API",
|
||||
"global_admin": true,
|
||||
"enabled": true,
|
||||
"change_pw_next_login": false,
|
||||
"enable_imap": true,
|
||||
"enable_pop": true,
|
||||
"allow_spoofing": false,
|
||||
"forward_enabled": false,
|
||||
"reply_enabled": false,
|
||||
"displayed_name": "admin",
|
||||
"spam_enabled": true,
|
||||
"spam_mark_as_read": true
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Created admin user (admin@mailu.io) successfully"
|
||||
|
||||
# Test if creating duplicate returns 409 HTTP response.
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://localhost/api/v1/user' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"email": "admin@mailu.io",
|
||||
"raw_password": "password",
|
||||
"comment": "created for testing RESTful API",
|
||||
"global_admin": true,
|
||||
"enabled": true,
|
||||
"change_pw_next_login": false,
|
||||
"enable_imap": true,
|
||||
"enable_pop": true,
|
||||
"allow_spoofing": false,
|
||||
"forward_enabled": false,
|
||||
"reply_enabled": false,
|
||||
"displayed_name": "admin",
|
||||
"spam_enabled": true,
|
||||
"spam_mark_as_read": true
|
||||
}' | grep 409
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "OK. Failed creating duplicate user."
|
||||
|
||||
# create user user@mailu.io
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://localhost/api/v1/user' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"email": "user@mailu.io",
|
||||
"raw_password": "password",
|
||||
"comment": "created for testing RESTful API",
|
||||
"global_admin": false,
|
||||
"enabled": true,
|
||||
"change_pw_next_login": false,
|
||||
"enable_imap": true,
|
||||
"enable_pop": true,
|
||||
"allow_spoofing": false,
|
||||
"forward_enabled": false,
|
||||
"reply_enabled": false,
|
||||
"displayed_name": "admin",
|
||||
"spam_enabled": true,
|
||||
"spam_mark_as_read": true
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Created user (user@mailu.io) successfully"
|
||||
|
||||
echo "Finished 00_create_users.sh"
|
80
tests/compose/api/01_test_user_interfaces.sh
Executable file
80
tests/compose/api/01_test_user_interfaces.sh
Executable file
@ -0,0 +1,80 @@
|
||||
echo "Test user interfaces"
|
||||
# create user user@mailu.io for testing deletion
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://localhost/api/v1/user' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"email": "user2@mailu.io",
|
||||
"raw_password": "password",
|
||||
"comment": "created for testing RESTful API",
|
||||
"global_admin": false,
|
||||
"enabled": true,
|
||||
"change_pw_next_login": false,
|
||||
"enable_imap": true,
|
||||
"enable_pop": true,
|
||||
"allow_spoofing": false,
|
||||
"forward_enabled": false,
|
||||
"reply_enabled": false,
|
||||
"displayed_name": "admin",
|
||||
"spam_enabled": true,
|
||||
"spam_mark_as_read": true
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "created user (user2@mailu.io) successfully"
|
||||
|
||||
#delete user2@mailu.io
|
||||
curl --silent --insecure -X 'DELETE' \
|
||||
'https://localhost/api/v1/user/user2%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Deleted user2 (user2@mailu.io) successfully"
|
||||
|
||||
#Check if updating user works
|
||||
curl --silent --insecure -X 'PATCH' \
|
||||
'https://localhost/api/v1/user/user%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "updated_comment"
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Updated user(user@mailu.io) successfully"
|
||||
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://localhost/api/v1/user/user%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep updated_comment
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Confirmed that comment attribute of user was correctly updated"
|
||||
|
||||
# try get all users. At this moment we should have 2 users total
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://localhost/api/v1/user' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep -o "email" | grep -c "email" | grep 2
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all users successfully"
|
||||
|
||||
echo "Finished 01_test_user_interfaces.sh"
|
145
tests/compose/api/02_test_domain_interfaces.sh
Executable file
145
tests/compose/api/02_test_domain_interfaces.sh
Executable file
@ -0,0 +1,145 @@
|
||||
echo "Test Domain interfaces"
|
||||
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://localhost/api/v1/domain' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"name": "mailu2.io",
|
||||
"comment": "internal domain for testing",
|
||||
"max_users": -1,
|
||||
"max_aliases": -1,
|
||||
"max_quota_bytes": 0,
|
||||
"signup_enabled": false
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Domain mail2.io has been created successfully"
|
||||
|
||||
curl --silent --insecure -X 'PATCH' \
|
||||
'https://localhost/api/v1/domain/mailu2.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "updated_domain"
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Domain mail2.io has been updated"
|
||||
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://localhost/api/v1/domain/mailu2.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep updated_domain
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Confirmed that comment attribute of domain mailu2.io was correctly updated"
|
||||
|
||||
# try get all domains. At this moment we should have 2 domains total
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://localhost/api/v1/domain' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep -o "name" | grep -c "name" | grep 2
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all domains successfully"
|
||||
|
||||
# try create dkim keys
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/domain/mailu2.io/dkim' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-d '' \
|
||||
| grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "dkim keys were created successfully for domain mailu2.io"
|
||||
|
||||
# try deleting a domain
|
||||
curl --silent --insecure -X 'DELETE' \
|
||||
'https://localhost/api/v1/domain/mailu2.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Domain mailu2.io was deleted successfully"
|
||||
|
||||
# try looking up all users of a domain. There should be 2 users.
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/domain/mailu.io/users' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o "email" | grep -c "email" | grep 2
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all users of domain mailu.io successfully"
|
||||
|
||||
|
||||
#### Alternatives
|
||||
|
||||
#try to create an alternative
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/alternative' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"name": "mailu2.io",
|
||||
"domain": "mailu.io"
|
||||
}' | grep 200
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Alternative mailu2.io for domain mailu.io was created successfully"
|
||||
|
||||
# try get all alternatives. At this moment we should have 1 alternative total
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://localhost/api/v1/alternative' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer apitest' \
|
||||
| grep -o "name" | grep -c "name" | grep 1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all alternatives successfully"
|
||||
|
||||
# try to check if an alternative exists
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/alternative/mailu2.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep '{"name": "mailu2.io", "domain": "mailu.io"}'
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Lookup for alternative mailu2.io was successful"
|
||||
|
||||
# try to delete an alternative
|
||||
curl --silent --insecure -X 'DELETE' \
|
||||
'https://mailutest/api/v1/alternative/mailu2.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest'
|
||||
|
||||
echo "Finshed 02_test_domain_interfaces.sh"
|
107
tests/compose/api/03_test_token_interfaces.sh
Executable file
107
tests/compose/api/03_test_token_interfaces.sh
Executable file
@ -0,0 +1,107 @@
|
||||
echo "start token tests"
|
||||
|
||||
# Try creating a token /token
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/token' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"email": "user@mailu.io",
|
||||
"comment": "my token related comment",
|
||||
"AuthorizedIP": [
|
||||
"203.0.113.0/24",
|
||||
"203.2.114.2/32"
|
||||
]
|
||||
}' | grep '"token": "'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "created a token for user@mailu.io successfully"
|
||||
|
||||
# Try create a token for a specific user /tokenuser/{email}
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/tokenuser/user%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "token test"
|
||||
}' | grep '"token": "'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "created a second token for user@mailu.io successfully"
|
||||
|
||||
# Try retrieving all tokens /token. We expect to retrieve 2 in total.
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/token' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o "id" | grep -c "id" | grep 2
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all tokens (2 in total) successfully"
|
||||
|
||||
# Try finding a specific token /token/{token_id}
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/token/2' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep '"id": 2'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved token with id 2 successfully"
|
||||
|
||||
# Try deleting a token /token/{token_id}
|
||||
curl --silent --insecure -X 'DELETE' \
|
||||
'https://mailutest/api/v1/token/1' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Deleted token with id 1 successfully"
|
||||
|
||||
# Try updating a token /token/{token_id}
|
||||
curl --silent --insecure -X 'PATCH' \
|
||||
'https://mailutest/api/v1/token/2' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "updated_comment",
|
||||
"AuthorizedIP": [
|
||||
"203.0.112.0/24"
|
||||
]
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Updated token with id 2 successfully"
|
||||
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/token/2' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep 'comment": "updated_comment"'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Confirmed that comment field of token with id 2 was correctly updated"
|
||||
|
||||
# Try looking up all tokens of a specific user /tokenuser/{email}
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/tokenuser/user%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o "id" | grep -c "id" | grep 1
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all tokens (1 in total) for user@mailu.io successfully"
|
||||
|
||||
echo "Finished 03_test_token_interfaces.sh"
|
98
tests/compose/api/04_test_relay_interfaces.sh
Executable file
98
tests/compose/api/04_test_relay_interfaces.sh
Executable file
@ -0,0 +1,98 @@
|
||||
echo "Start 04_test_relay_interfaces.sh"
|
||||
|
||||
# Try creating a new relay /relay
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/relay' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"name": "relay1.mailu.io",
|
||||
"smtp": "relay1.mailu.io:8755",
|
||||
"comment": "backup relay1"
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "created a relay for domain relay1.mailu.io successfully"
|
||||
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/relay' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"name": "relay2.mailu.io",
|
||||
"comment": "backup relay2"
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "created a relay for domain relay2.mailu.io successfully"
|
||||
|
||||
# Try retrieving all relays /relay. We expect to retrieve 2 in total
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/relay' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o '"name":' | grep -c '"name":' | grep 2
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved all relays (2 in total) successfully"
|
||||
|
||||
# Try looking up a specific relay /relay/{name}
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/relay/relay1.mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep '"name": "relay1.mailu.io"'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrieved the specified relay (relay1.mailu.io) successfully"
|
||||
|
||||
# Try deleting a specific relay /relay/{name}
|
||||
curl -silent --insecure -X 'DELETE' \
|
||||
'https://mailutest/api/v1/relay/relay2.mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Deleted relay2.mailu.io successfully"
|
||||
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/relay' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o '"name":' | grep -c '"name":' | grep 1
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "confirmed we only have 1 relay now"
|
||||
|
||||
# Try updating a specific relay /relay/{name}
|
||||
curl --silent --insecure -X 'PATCH' \
|
||||
'https://mailutest/api/v1/relay/relay1.mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"smtp": "anotherName",
|
||||
"comment": "updated_comment"
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "update of relay was succcessful"
|
||||
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/relay/relay1.mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep anotherName | grep updated_comment
|
||||
echo "confirmed that smtp attribute and comment attribute were correctly updated"
|
||||
|
||||
echo "Finished 04_test_relay_interfaces.sh"
|
111
tests/compose/api/05_test_alias_interfaces.sh
Executable file
111
tests/compose/api/05_test_alias_interfaces.sh
Executable file
@ -0,0 +1,111 @@
|
||||
# try create, find, lookup, delete
|
||||
|
||||
echo "Start 05_test_alias_interfaces.sh"
|
||||
|
||||
# Try creating a new alias /alias
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/alias' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "test alias for user@mailu.io and admin@mailu.io",
|
||||
"destination": [
|
||||
"user@mailu.io",
|
||||
"admin@mailu.io"
|
||||
],
|
||||
"wildcard": false,
|
||||
"email": "test@mailu.io"
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Created alias test@mailu.io succcessfully for user@mailu.io and admin@mailu.io"
|
||||
|
||||
curl --silent --insecure -X 'POST' \
|
||||
'https://mailutest/api/v1/alias' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "test2 alias for user@mailu.io",
|
||||
"destination": [
|
||||
"user@mailu.io"
|
||||
],
|
||||
"wildcard": false,
|
||||
"email": "test2@mailu.io"
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Created alias test2@mailu.io succcessfully for user@mailu.io "
|
||||
|
||||
# Try retrieving all aliases /alias. We expect to retrieve 2
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/alias' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o '"destination":' | grep -c '"destination":' | grep 2
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Successfully retrieved 2 aliases"
|
||||
|
||||
# Try looking up the aliases for a specific domain /alias/destination/{domain}. We expect to retrieve 2
|
||||
curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/alias/destination/mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep -o '"destination":' | grep -c '"destination":' | grep 2
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Successfully retrieved 2 aliases"
|
||||
|
||||
# Try deleting a specific alias /alias/{alias}
|
||||
curl --silent --insecure -X 'DELETE' \
|
||||
'https://mailutest/api/v1/alias/test2%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
| grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Deleted alias test2@mailu.io succcessfully"
|
||||
|
||||
# Try updating a specific alias /alias/{alias}
|
||||
curl --silent --insecure -X 'PATCH' \
|
||||
'https://mailutest/api/v1/alias/test%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"comment": "updated_comment",
|
||||
"destination": [
|
||||
"user@mailu.io"
|
||||
],
|
||||
"wildcard": true
|
||||
}' | grep 200
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Updated alias test2@mailu.io succcessfully"
|
||||
|
||||
# Try looking up a specific alias /alias/{alias}.
|
||||
#Check if values were updated correctyly in previous step.
|
||||
response=$(curl --silent --insecure -X 'GET' \
|
||||
'https://mailutest/api/v1/alias/test%40mailu.io' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: apitest')
|
||||
echo $response | grep 'admin@mailu.io'
|
||||
if [ $? -ne 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Confirmed that destination admin@mailu.io is removed from alias test@mailu.io"
|
||||
echo $response | grep 'updated_comment'
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Confirmed that comment attribute is updated successfully"
|
||||
|
||||
echo "Finished 05_test_alias_interfaces.sh"
|
112
tests/compose/api/docker-compose.yml
Normal file
112
tests/compose/api/docker-compose.yml
Normal file
@ -0,0 +1,112 @@
|
||||
# This file is auto-generated by the Mailu configuration wizard.
|
||||
# Please read the documentation before attempting any change.
|
||||
# Generated for compose flavor
|
||||
|
||||
version: '3.6'
|
||||
|
||||
services:
|
||||
|
||||
# External dependencies
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- "/mailu/redis:/data"
|
||||
|
||||
# Core services
|
||||
front:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-local}
|
||||
restart: always
|
||||
env_file: mailu.env
|
||||
logging:
|
||||
driver: json-file
|
||||
ports:
|
||||
- "127.0.0.1:80:80"
|
||||
- "127.0.0.1:443:443"
|
||||
- "127.0.0.1:25:25"
|
||||
- "127.0.0.1:465:465"
|
||||
- "127.0.0.1:587:587"
|
||||
- "127.0.0.1:110:110"
|
||||
- "127.0.0.1:995:995"
|
||||
- "127.0.0.1:143:143"
|
||||
- "127.0.0.1:993:993"
|
||||
- "127.0.0.1:4190:4190"
|
||||
volumes:
|
||||
- "/mailu/certs:/certs"
|
||||
|
||||
admin:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}admin:${MAILU_VERSION:-local}
|
||||
restart: always
|
||||
env_file: mailu.env
|
||||
volumes:
|
||||
- "/mailu/data:/data"
|
||||
- "/mailu/dkim:/dkim"
|
||||
dns:
|
||||
- 192.168.203.254
|
||||
depends_on:
|
||||
- redis
|
||||
- resolver
|
||||
|
||||
imap:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}dovecot:${MAILU_VERSION:-local}
|
||||
restart: always
|
||||
env_file: mailu.env
|
||||
volumes:
|
||||
- "/mailu/mail:/mail"
|
||||
- "/mailu/overrides:/overrides"
|
||||
depends_on:
|
||||
- front
|
||||
|
||||
smtp:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-local}
|
||||
restart: always
|
||||
env_file: mailu.env
|
||||
volumes:
|
||||
- "/mailu/overrides:/overrides"
|
||||
depends_on:
|
||||
- front
|
||||
|
||||
oletools:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
|
||||
hostname: oletools
|
||||
restart: always
|
||||
networks:
|
||||
- noinet
|
||||
|
||||
antispam:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
|
||||
restart: always
|
||||
env_file: mailu.env
|
||||
networks:
|
||||
- default
|
||||
- noinet
|
||||
volumes:
|
||||
- "/mailu/filter:/var/lib/rspamd"
|
||||
- "/mailu/dkim:/dkim"
|
||||
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
|
||||
depends_on:
|
||||
- front
|
||||
|
||||
# Optional services
|
||||
|
||||
resolver:
|
||||
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-local}
|
||||
env_file: mailu.env
|
||||
restart: always
|
||||
networks:
|
||||
default:
|
||||
ipv4_address: 192.168.203.254
|
||||
|
||||
# Webmail
|
||||
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 192.168.203.0/24
|
||||
noinet:
|
||||
driver: bridge
|
||||
internal: true
|
151
tests/compose/api/mailu.env
Normal file
151
tests/compose/api/mailu.env
Normal file
@ -0,0 +1,151 @@
|
||||
# Mailu main configuration file
|
||||
#
|
||||
# Generated for compose flavor
|
||||
#
|
||||
# This file is autogenerated by the configuration management wizard.
|
||||
# For a detailed list of configuration variables, see the documentation at
|
||||
# https://mailu.io
|
||||
|
||||
###################################
|
||||
# Common configuration variables
|
||||
###################################
|
||||
|
||||
# Set this to the path where Mailu data and configuration is stored
|
||||
# This variable is now set directly in `docker-compose.yml by the setup utility
|
||||
# ROOT=/mailu
|
||||
|
||||
# Mailu version to run (1.0, 1.1, etc. or master)
|
||||
#VERSION=master
|
||||
|
||||
# Set to a randomly generated 16 bytes string
|
||||
SECRET_KEY=HGZCYGVI6FVG31HS
|
||||
|
||||
# Address where listening ports should bind
|
||||
# This variables are now set directly in `docker-compose.yml by the setup utility
|
||||
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
|
||||
# PUBLIC_IPV6= (default: ::1)
|
||||
|
||||
# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
|
||||
SUBNET=192.168.203.0/24
|
||||
|
||||
# Main mail domain
|
||||
DOMAIN=mailu.io
|
||||
|
||||
# Hostnames for this server, separated with commas
|
||||
HOSTNAMES=localhost
|
||||
|
||||
# Postmaster local part (will append the main mail domain)
|
||||
POSTMASTER=admin
|
||||
|
||||
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
|
||||
TLS_FLAVOR=cert
|
||||
|
||||
# Authentication rate limit (per source IP address)
|
||||
AUTH_RATELIMIT=10/minute;1000/hour
|
||||
|
||||
# Opt-out of statistics, replace with "True" to opt out
|
||||
DISABLE_STATISTICS=False
|
||||
|
||||
###################################
|
||||
# Optional features
|
||||
###################################
|
||||
|
||||
# Expose the admin interface (value: true, false)
|
||||
ADMIN=true
|
||||
|
||||
# Choose which webmail to run if any (values: roundcube, snappymail, none)
|
||||
WEBMAIL=none
|
||||
|
||||
# Dav server implementation (value: radicale, none)
|
||||
WEBDAV=none
|
||||
|
||||
# Antivirus solution (value: clamav, none)
|
||||
#ANTIVIRUS=none
|
||||
|
||||
#Antispam solution
|
||||
ANTISPAM=none
|
||||
|
||||
#RESTful API
|
||||
API=true
|
||||
|
||||
# Scan Macros solution (value: true, false)
|
||||
SCAN_MACROS=True
|
||||
|
||||
###################################
|
||||
# Mail settings
|
||||
###################################
|
||||
|
||||
# Message size limit in bytes
|
||||
# Default: accept messages up to 50MB
|
||||
MESSAGE_SIZE_LIMIT=50000000
|
||||
|
||||
# Networks granted relay permissions
|
||||
# Use this with care, all hosts in this networks will be able to send mail without authentication!
|
||||
RELAYNETS=
|
||||
|
||||
# Will relay all outgoing mails if configured
|
||||
RELAYHOST=
|
||||
|
||||
# Show fetchmail functionality in admin interface
|
||||
FETCHMAIL_ENABLED=false
|
||||
|
||||
# Fetchmail delay
|
||||
FETCHMAIL_DELAY=600
|
||||
|
||||
# Recipient delimiter, character used to delimiter localpart from custom address part
|
||||
RECIPIENT_DELIMITER=+
|
||||
|
||||
# DMARC rua and ruf email
|
||||
DMARC_RUA=admin
|
||||
DMARC_RUF=admin
|
||||
|
||||
|
||||
# Maildir Compression
|
||||
# choose compression-method, default: none (value: gz, bz2, lz4, zstd)
|
||||
COMPRESSION=
|
||||
# change compression-level, default: 6 (value: 1-9)
|
||||
COMPRESSION_LEVEL=
|
||||
|
||||
###################################
|
||||
# Web settings
|
||||
###################################
|
||||
|
||||
# Path to the admin interface if enabled
|
||||
WEB_ADMIN=/admin
|
||||
|
||||
# Path to the webmail if enabled
|
||||
WEB_WEBMAIL=/webmail
|
||||
|
||||
WEB_API=/api
|
||||
|
||||
# Website name
|
||||
SITENAME=Mailu
|
||||
|
||||
# Linked Website URL
|
||||
WEBSITE=https://mailu.io
|
||||
|
||||
|
||||
|
||||
###################################
|
||||
# Advanced settings
|
||||
###################################
|
||||
|
||||
# Log driver for front service. Possible values:
|
||||
# json-file (default)
|
||||
# journald (On systemd platforms, useful for Fail2Ban integration)
|
||||
# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker compose log` for front!)
|
||||
# LOG_DRIVER=json-file
|
||||
|
||||
# Docker-compose project name, this will prepended to containers names.
|
||||
COMPOSE_PROJECT_NAME=mailu
|
||||
|
||||
# Header to take the real ip from
|
||||
REAL_IP_HEADER=
|
||||
|
||||
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
|
||||
REAL_IP_FROM=
|
||||
|
||||
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
|
||||
REJECT_UNLISTED_RECIPIENT=
|
||||
|
||||
API_TOKEN=apitest
|
Loading…
x
Reference in New Issue
Block a user