mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
@@ -254,7 +254,7 @@ def admin_menu(sender, location, user, **kwargs):
|
||||
if location != "top_menu":
|
||||
return []
|
||||
entries = []
|
||||
if user.has_perm("admin.view_domains"):
|
||||
if user.has_perm("admin.view_domain"):
|
||||
entries += [
|
||||
{"name": "domains",
|
||||
"url": reverse("admin:domain_list"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Modoboa main management command."""
|
||||
|
||||
from subcommand.base import SubcommandCommand
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from .subcommands._export import ExportCommand
|
||||
from .subcommands._import import ImportCommand
|
||||
@@ -9,13 +9,13 @@ from .subcommands._mx import CheckMXRecords
|
||||
from .subcommands._repair import Repair
|
||||
|
||||
|
||||
class Command(SubcommandCommand):
|
||||
class Command(BaseCommand):
|
||||
"""Top management command for modoboa.
|
||||
|
||||
$ python manage.py modo
|
||||
"""
|
||||
|
||||
help = "Modoboa top management command." # NOQA:A003
|
||||
help = "Modoboa top management command."
|
||||
|
||||
subcommands = {
|
||||
"export": ExportCommand,
|
||||
@@ -24,3 +24,27 @@ class Command(SubcommandCommand):
|
||||
"manage_dkim_keys": ManageDKIMKeys,
|
||||
"repair": Repair,
|
||||
}
|
||||
|
||||
def add_arguments(self, parser):
|
||||
subparsers = parser.add_subparsers(
|
||||
dest='subcommand', title='subcommands'
|
||||
)
|
||||
# required argument is added in Python 3.7
|
||||
subparsers.required = True
|
||||
|
||||
for command_name, command_class in self.subcommands.items():
|
||||
command = command_class(self.stdout, self.stderr)
|
||||
command.style = self.style
|
||||
|
||||
subparser = subparsers.add_parser(
|
||||
command_name, help=command_class.help
|
||||
)
|
||||
# This is needed to output console friendly errors
|
||||
subparser.called_from_command_line = self._called_from_command_line
|
||||
|
||||
command.add_arguments(subparser)
|
||||
|
||||
subparser.set_defaults(command=command)
|
||||
|
||||
def execute(self, *args, **options):
|
||||
return options.pop('command').execute(*args, **options)
|
||||
|
||||
@@ -19,7 +19,6 @@ class Migration(migrations.Migration):
|
||||
],
|
||||
options={
|
||||
'ordering': ['address'],
|
||||
'permissions': (('view_aliases', 'View aliases'),),
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
@@ -47,7 +46,6 @@ class Migration(migrations.Migration):
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
'permissions': (('view_domain', 'View domain'), ('view_domains', 'View domains')),
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
@@ -58,9 +56,6 @@ class Migration(migrations.Migration):
|
||||
('name', models.CharField(help_text='The alias name', unique=True, max_length=100, verbose_name='name')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Check to activate this alias', verbose_name='enabled')),
|
||||
],
|
||||
options={
|
||||
'permissions': (('view_domaliases', 'View domain aliases'),),
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@@ -71,9 +66,6 @@ class Migration(migrations.Migration):
|
||||
('quota', models.PositiveIntegerField()),
|
||||
('use_domain_quota', models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
'permissions': (('view_mailboxes', 'View mailboxes'),),
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Generated by Django 2.1.15 on 2020-01-13 09:22
|
||||
|
||||
import sys
|
||||
|
||||
from django.db import IntegrityError, migrations, transaction
|
||||
|
||||
VIEW_PERMISSION_CODENAMES = {
|
||||
'view_aliases': 'view_alias',
|
||||
'view_domaliases': 'view_domainalias',
|
||||
'view_mailboxes': 'view_mailbox',
|
||||
}
|
||||
|
||||
|
||||
def rename_view_permissions(apps, schema_editor):
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
db_alias = schema_editor.connection.alias
|
||||
# Iterate over permissions and try to rename the codename
|
||||
for perm in Permission.objects.using(db_alias).filter(
|
||||
codename__in=VIEW_PERMISSION_CODENAMES.keys()
|
||||
):
|
||||
codename = perm.codename
|
||||
try:
|
||||
with transaction.atomic():
|
||||
perm.codename = VIEW_PERMISSION_CODENAMES[perm.codename]
|
||||
perm.save()
|
||||
except IntegrityError:
|
||||
print(
|
||||
(
|
||||
"Unable to rename '{}' to '{}', a permission with this "
|
||||
"codename already exists.".format(codename, perm.codename)
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('admin', '0014_auto_20181017_1628'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(rename_view_permissions),
|
||||
]
|
||||
@@ -48,9 +48,6 @@ class Alias(AdminObject):
|
||||
_objectname = "MailboxAlias"
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_aliases", "View aliases"),
|
||||
)
|
||||
ordering = ["address"]
|
||||
unique_together = (("address", "internal"), )
|
||||
app_label = "admin"
|
||||
|
||||
@@ -63,10 +63,6 @@ class Domain(AdminObject):
|
||||
dkim_private_key_path = models.CharField(max_length=254, blank=True)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_domain", "View domain"),
|
||||
("view_domains", "View domains"),
|
||||
)
|
||||
ordering = ["name"]
|
||||
app_label = "admin"
|
||||
|
||||
|
||||
@@ -49,9 +49,6 @@ class DomainAlias(AdminObject):
|
||||
objects = DomainAliasManager()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_domaliases", "View domain aliases"),
|
||||
)
|
||||
app_label = "admin"
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -104,9 +104,6 @@ class Mailbox(AdminObject):
|
||||
objects = MailboxManager()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_mailboxes", "View mailboxes"),
|
||||
)
|
||||
app_label = "admin"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -318,8 +318,7 @@ class DomainTestCase(ModoTestCase):
|
||||
domain = Domain.objects.get(name="test2.com")
|
||||
url = reverse("admin:domain_detail", args=[domain.pk])
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(response.url.startswith("/accounts/login"))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_domain_quota_list_view(self):
|
||||
"""Test quota list view."""
|
||||
|
||||
+55
-53
@@ -1,6 +1,6 @@
|
||||
"""Admin urls."""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
from .views import (
|
||||
@@ -8,71 +8,73 @@ from .views import (
|
||||
identity as identity_views, import_ as import_views
|
||||
)
|
||||
|
||||
app_name = "admin"
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', domain_views.index, name="index"),
|
||||
url(r'^domains/$', domain_views.domains, name="domain_list"),
|
||||
url(r'^domains/(?P<pk>\d+)/$', views.DomainDetailView.as_view(),
|
||||
name="domain_detail"),
|
||||
url(r'^domains/(?P<pk>\d+)/dnsbl/$', views.DNSBLDomainDetailView.as_view(),
|
||||
name="dnsbl_domain_detail"),
|
||||
url(r'^domains/(?P<pk>\d+)/mx/$', views.MXDomainDetailView.as_view(),
|
||||
name="mx_domain_detail"),
|
||||
url(r'^domains/list/$', domain_views._domains, name="_domain_list"),
|
||||
url(r'^domains/quotas/$', domain_views.list_quotas,
|
||||
name="domain_quota_list"),
|
||||
url(r'^domains/flatlist/$', domain_views.domains_list,
|
||||
name="domain_flat_list"),
|
||||
url(r'^domains/new/', domain_views.newdomain, name="domain_add"),
|
||||
url(r'^domains/(?P<dom_id>\d+)/edit/$', domain_views.editdomain,
|
||||
name="domain_change"),
|
||||
url(r'^domains/(?P<dom_id>\d+)/delete/$', domain_views.deldomain,
|
||||
name="domain_delete"),
|
||||
url(r'^domains/page/$', domain_views.get_next_page,
|
||||
name="domain_page"),
|
||||
path('', domain_views.index, name="index"),
|
||||
path('domains/', domain_views.domains, name="domain_list"),
|
||||
path('domains/<int:pk>/', views.DomainDetailView.as_view(),
|
||||
name="domain_detail"),
|
||||
path('domains/<int:pk>/dnsbl/', views.DNSBLDomainDetailView.as_view(),
|
||||
name="dnsbl_domain_detail"),
|
||||
path('domains/<int:pk>/mx/', views.MXDomainDetailView.as_view(),
|
||||
name="mx_domain_detail"),
|
||||
path('domains/list/', domain_views._domains, name="_domain_list"),
|
||||
path('domains/quotas/', domain_views.list_quotas,
|
||||
name="domain_quota_list"),
|
||||
path('domains/flatlist/', domain_views.domains_list,
|
||||
name="domain_flat_list"),
|
||||
path('domains/new/', domain_views.newdomain, name="domain_add"),
|
||||
path('domains/<int:dom_id>/edit/', domain_views.editdomain,
|
||||
name="domain_change"),
|
||||
path('domains/<int:dom_id>/delete/', domain_views.deldomain,
|
||||
name="domain_delete"),
|
||||
path('domains/page/', domain_views.get_next_page,
|
||||
name="domain_page"),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^permissions/remove/$', identity_views.remove_permission,
|
||||
name="permission_remove"),
|
||||
path('permissions/remove/', identity_views.remove_permission,
|
||||
name="permission_remove"),
|
||||
|
||||
url(r'^identities/$', identity_views.identities, name="identity_list"),
|
||||
url(r'^identities/list/$', identity_views._identities,
|
||||
name="_identity_list"),
|
||||
url(r'^identities/quotas/$', identity_views.list_quotas,
|
||||
name="identity_quota_list"),
|
||||
url(r'^identities/page/$', identity_views.get_next_page,
|
||||
name="identity_page"),
|
||||
path('identities/', identity_views.identities, name="identity_list"),
|
||||
path('identities/list/', identity_views._identities,
|
||||
name="_identity_list"),
|
||||
path('identities/quotas/', identity_views.list_quotas,
|
||||
name="identity_quota_list"),
|
||||
path('identities/page/', identity_views.get_next_page,
|
||||
name="identity_page"),
|
||||
|
||||
url(r'^accounts/list/$', identity_views.accounts_list,
|
||||
name="account_list"),
|
||||
url(r'^accounts/new/$', identity_views.newaccount, name="account_add"),
|
||||
url(r'^accounts/(?P<pk>\d+)/$', views.AccountDetailView.as_view(),
|
||||
name="account_detail"),
|
||||
url(r'^accounts/(?P<pk>\d+)/edit/$', identity_views.editaccount,
|
||||
name="account_change"),
|
||||
url(r'^accounts/(?P<pk>\d+)/delete/$', identity_views.delaccount,
|
||||
name="account_delete"),
|
||||
path('accounts/list/', identity_views.accounts_list,
|
||||
name="account_list"),
|
||||
path('accounts/new/', identity_views.newaccount, name="account_add"),
|
||||
path('accounts/<int:pk>/', views.AccountDetailView.as_view(),
|
||||
name="account_detail"),
|
||||
path('accounts/<int:pk>/edit/', identity_views.editaccount,
|
||||
name="account_change"),
|
||||
path('accounts/<int:pk>/delete/', identity_views.delaccount,
|
||||
name="account_delete"),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^aliases/new/$', alias_views.newalias, name="alias_add"),
|
||||
url(r'^aliases/(?P<pk>\d+)/$', views.AliasDetailView.as_view(),
|
||||
name="alias_detail"),
|
||||
url(r'^aliases/(?P<alid>\d+)/edit/$', alias_views.editalias,
|
||||
name="alias_change"),
|
||||
url(r'^aliases/delete/$', alias_views.delalias, name="alias_delete"),
|
||||
path('aliases/new/', alias_views.newalias, name="alias_add"),
|
||||
path('aliases/<int:pk>/', views.AliasDetailView.as_view(),
|
||||
name="alias_detail"),
|
||||
path('aliases/<int:alid>/edit/', alias_views.editalias,
|
||||
name="alias_change"),
|
||||
path('aliases/delete/', alias_views.delalias, name="alias_delete"),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^domains/import/$', import_views.import_domains,
|
||||
name="domain_import"),
|
||||
url(r'^identities/import/$', import_views.import_identities,
|
||||
name="identity_import"),
|
||||
path('domains/import/', import_views.import_domains,
|
||||
name="domain_import"),
|
||||
path('identities/import/', import_views.import_identities,
|
||||
name="identity_import"),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^domains/export/$', export_views.export_domains,
|
||||
name="domain_export"),
|
||||
url(r'^identities/export/$', export_views.export_identities,
|
||||
name="identity_export"),
|
||||
path('domains/export/', export_views.export_domains,
|
||||
name="domain_export"),
|
||||
path('identities/export/', export_views.export_identities,
|
||||
name="identity_export"),
|
||||
]
|
||||
|
||||
@@ -34,8 +34,8 @@ def index(request):
|
||||
|
||||
@login_required
|
||||
@user_passes_test(
|
||||
lambda u: u.has_perm("admin.view_domains") or
|
||||
u.has_perm("admin.view_mailboxes")
|
||||
lambda u: u.has_perm("admin.view_domain") or
|
||||
u.has_perm("admin.view_mailbox")
|
||||
)
|
||||
def _domains(request):
|
||||
sort_order, sort_dir = get_sort_order(request.GET, "name")
|
||||
@@ -92,8 +92,8 @@ def _domains(request):
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def domains(request, tplname="admin/domains.html"):
|
||||
if not request.user.has_perm("admin.view_domains"):
|
||||
if request.user.has_perm("admin.view_mailboxes"):
|
||||
if not request.user.has_perm("admin.view_domain"):
|
||||
if request.user.has_perm("admin.view_mailbox"):
|
||||
return HttpResponseRedirect(
|
||||
reverse("admin:identity_list")
|
||||
)
|
||||
@@ -113,8 +113,8 @@ def domains(request, tplname="admin/domains.html"):
|
||||
|
||||
@login_required
|
||||
@user_passes_test(
|
||||
lambda u: u.has_perm("admin.view_domains") or
|
||||
u.has_perm("admin.view_mailboxes") or
|
||||
lambda u: u.has_perm("admin.view_domain") or
|
||||
u.has_perm("admin.view_mailbox") or
|
||||
u.has_perm("admin.add_domain")
|
||||
)
|
||||
def get_next_page(request):
|
||||
@@ -170,7 +170,7 @@ def newdomain(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.view_domains")
|
||||
@permission_required("admin.view_domain")
|
||||
@reversion.create_revision()
|
||||
def editdomain(request, dom_id):
|
||||
"""Edit domain view."""
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"homepage": "https://github.com/wordnik/swagger-ui",
|
||||
"version": "3.24.3",
|
||||
"_release": "3.24.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v3.24.3",
|
||||
"commit": "a86a63db045782dbfa48e056888fe85fc4ded050"
|
||||
},
|
||||
"_source": "https://github.com/wordnik/swagger-ui.git",
|
||||
"_target": "3.24.3",
|
||||
"_originalSource": "swagger-ui",
|
||||
"_direct": true
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
node_modules
|
||||
.idea
|
||||
.vscode
|
||||
.deps_check
|
||||
.DS_Store
|
||||
.nyc_output
|
||||
npm-debug.log*
|
||||
.eslintcache
|
||||
*.iml
|
||||
selenium-debug.log
|
||||
test/e2e/db.json
|
||||
docs/_book
|
||||
|
||||
flavors/**/dist/*
|
||||
|
||||
# Cypress
|
||||
test/e2e-cypress/screenshots
|
||||
test/e2e-cypress/videos
|
||||
|
||||
# Ignore files that we don't need for Modoboa
|
||||
.agignore
|
||||
.babelrc
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.eslintrc
|
||||
.gitattributes
|
||||
.npmignore
|
||||
.prettierrc.yaml
|
||||
Dockerfile
|
||||
composer.json
|
||||
cypress.json
|
||||
renovate.json
|
||||
snapcraft.yaml
|
||||
swagger-config.yaml
|
||||
.github/
|
||||
dev-helpers/
|
||||
docker/
|
||||
docs/
|
||||
flavors/
|
||||
release/
|
||||
src/
|
||||
swagger-ui-dist-package/
|
||||
test/
|
||||
webpack/
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
Copyright 2019 SmartBear Software
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# <img src="https://raw.githubusercontent.com/swagger-api/swagger.io/wordpress/images/assets/SWU-logo-clr.png" width="300">
|
||||
|
||||
[](http://badge.fury.io/js/swagger-ui)
|
||||
[](https://jenkins.swagger.io/view/OSS%20-%20JavaScript/job/oss-swagger-ui-master/)
|
||||
[](https://jenkins.swagger.io/job/oss-swagger-ui-security-audit/lastBuild/console)
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
**👉🏼 Want to score an easy open-source contribution?** Check out our [Good first issue](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22) label.
|
||||
|
||||
**🕰️ Looking for the older version of Swagger UI?** Refer to the [*2.x* branch](https://github.com/swagger-api/swagger-ui/tree/2.x).
|
||||
|
||||
|
||||
This repository publishes to three different NPM modules:
|
||||
|
||||
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is a traditional npm module intended for use in single-page applications that are capable of resolving dependencies (via Webpack, Browserify, etc).
|
||||
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) is a dependency-free module that includes everything you need to serve Swagger UI in a server-side project, or a single-page application that can't resolve npm module dependencies.
|
||||
* [swagger-ui-react](https://www.npmjs.com/package/swagger-ui-react) is Swagger UI packaged as a React component for use in React applications.
|
||||
|
||||
We strongly suggest that you use `swagger-ui` instead of `swagger-ui-dist` if you're building a single-page application, since `swagger-ui-dist` is significantly larger.
|
||||
|
||||
|
||||
## Compatibility
|
||||
The OpenAPI Specification has undergone 5 revisions since initial creation in 2010. Compatibility between Swagger UI and the OpenAPI Specification is as follows:
|
||||
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.18.3 | 2018-08-03 | 2.0, 3.0 | [tag v3.18.3](https://github.com/swagger-api/swagger-ui/tree/v3.18.3)
|
||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
||||
2.0.24 | 2014-09-12 | 1.1, 1.2 | [tag v2.0.24](https://github.com/swagger-api/swagger-ui/tree/v2.0.24)
|
||||
1.0.13 | 2013-03-08 | 1.1, 1.2 | [tag v1.0.13](https://github.com/swagger-api/swagger-ui/tree/v1.0.13)
|
||||
1.0.1 | 2011-10-11 | 1.0, 1.1 | [tag v1.0.1](https://github.com/swagger-api/swagger-ui/tree/v1.0.1)
|
||||
|
||||
## Documentation
|
||||
|
||||
#### Usage
|
||||
- [Installation](docs/usage/installation.md)
|
||||
- [Configuration](docs/usage/configuration.md)
|
||||
- [CORS](docs/usage/cors.md)
|
||||
- [OAuth2](docs/usage/oauth2.md)
|
||||
- [Deep Linking](docs/usage/deep-linking.md)
|
||||
- [Limitations](docs/usage/limitations.md)
|
||||
- [Version detection](docs/usage/version-detection.md)
|
||||
|
||||
#### Customization
|
||||
- [Overview](docs/customization/overview.md)
|
||||
- [Plugin API](docs/customization/plugin-api.md)
|
||||
- [Custom layout](docs/customization/custom-layout.md)
|
||||
|
||||
#### Development
|
||||
- [Setting up](docs/development/setting-up.md)
|
||||
- [Scripts](docs/development/scripts.md)
|
||||
|
||||
##### Integration Tests
|
||||
|
||||
You will need JDK of version 7 or higher as instructed here
|
||||
https://nightwatchjs.org/gettingstarted/#selenium-server-setup
|
||||
|
||||
Integration tests can be run locally with `npm run e2e` - be sure you aren't running a dev server when testing!
|
||||
|
||||
### Browser support
|
||||
Swagger UI works in the latest versions of Chrome, Safari, Firefox, Edge and IE11.
|
||||
|
||||
### Known Issues
|
||||
|
||||
To help with the migration, here are the currently known issues with 3.X. This list will update regularly, and will not include features that were not implemented in previous versions.
|
||||
|
||||
- Only part of the parameters previously supported are available.
|
||||
- The JSON Form Editor is not implemented.
|
||||
- Support for `collectionFormat` is partial.
|
||||
- l10n (translations) is not implemented.
|
||||
- Relative path support for external files is not implemented.
|
||||
|
||||
## Security contact
|
||||
|
||||
Please disclose any security-related issues or vulnerabilities by emailing [security@swagger.io](mailto:security@swagger.io), instead of using the public issue tracker.
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2019 SmartBear Software
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Security Policy
|
||||
|
||||
If you believe you've found an exploitable security issue in Swagger UI,
|
||||
**please don't create a public issue**.
|
||||
|
||||
|
||||
## Supported versions
|
||||
|
||||
This is the list of versions of `swagger-ui` which are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported | Notes |
|
||||
| -------- | ------------------ | ---------------------- |
|
||||
| 3.x | :white_check_mark: | |
|
||||
| 2.x | :x: | End-of-life as of 2017 |
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
To report a vulnerability please send an email with the details to [security@swagger.io](mailto:security@swagger.io).
|
||||
|
||||
We'll acknowledge receipt of your report ASAP, and set expectations on how we plan to handle it.
|
||||
@@ -0,0 +1,60 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "https://petstore.swagger.io/v2/swagger.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+19447
File diff suppressed because it is too large
Load Diff
+168
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.24.3",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"homepage": "https://github.com/swagger-api/swagger-ui",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
"(in alphabetical order)",
|
||||
"Anna Bodnia <anna.bodnia@gmail.com>",
|
||||
"Buu Nguyen <buunguyen@gmail.com>",
|
||||
"Josh Ponelat <jponelat@gmail.com>",
|
||||
"Kyle Shockey <kyleshockey@gmail.com>",
|
||||
"Robert Barnwell <robert@robertismy.name>",
|
||||
"Sahar Jafari <shr.jafari@gmail.com>"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"automated-release": "release-it --config ./release/.release-it.json",
|
||||
"build": "run-p --aggregate-output build-core build-bundle build-standalone build-stylesheets",
|
||||
"build-bundle": "webpack --colors --config webpack/bundle.babel.js",
|
||||
"build-core": "webpack --colors --config webpack/core.babel.js",
|
||||
"build-standalone": "webpack --colors --config webpack/standalone.babel.js",
|
||||
"build-stylesheets": "webpack --colors --config webpack/stylesheets.babel.js",
|
||||
"predev": "npm install",
|
||||
"dev": "webpack-dev-server --config webpack/dev.babel.js",
|
||||
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
|
||||
"deps-size": "webpack -p --config webpack/bundle.babel.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
|
||||
"deps-check": "run-s deps-license deps-size",
|
||||
"lint": "eslint --cache --ext '.js,.jsx' src test",
|
||||
"lint-errors": "eslint --cache --quiet --ext '.js,.jsx' src test",
|
||||
"lint-fix": "eslint --cache --ext '.js,.jsx' src test --fix",
|
||||
"test": "run-s just-test-in-node e2e-cypress lint-errors",
|
||||
"test-in-node": "run-s lint-errors just-test-in-node",
|
||||
"just-test-in-node": "mocha --require test/mocha/setup.js --recursive --compilers js:@babel/register --require source-map-support 'test/mocha/**/*.{js,jsx}'",
|
||||
"test-e2e-cypress": "cypress run",
|
||||
"test-e2e-selenium": "sleep 3 && nightwatch test/e2e-selenium/scenarios/ --config test/e2e-selenium/nightwatch.json",
|
||||
"e2e-initial-render": "nightwatch test/e2e-selenium/scenarios/ --config test/e2e-selenium/nightwatch.json --group initial-render",
|
||||
"mock-api": "json-server --watch test/e2e-selenium/db.json --port 3204",
|
||||
"hot-e2e-cypress-server": "webpack-dev-server --config webpack/dev-e2e.babel.js --content-base test/e2e-cypress/static",
|
||||
"hot-e2e-selenium-server": "webpack-dev-server --config webpack/dev-e2e.babel.js --content-base test/e2e-selenium/static",
|
||||
"e2e-cypress": "run-p -r hot-e2e-cypress-server mock-api test-e2e-cypress",
|
||||
"e2e-selenium": "run-p -r hot-e2e-selenium-server mock-api test-e2e-selenium",
|
||||
"open-static": "node -e 'require(\"open\")(\"http://localhost:3002\")'",
|
||||
"security-audit": "run-s -sc security-audit:all security-audit:prod",
|
||||
"security-audit:prod": "npm-audit-ci-wrapper -p -t low",
|
||||
"security-audit:all": "npm-audit-ci-wrapper -t moderate",
|
||||
"serve-static": "http-server dist/ -i -a 0.0.0.0 -p 3002",
|
||||
"start": "npm-run-all --parallel serve-static open-static"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "^7.5.5",
|
||||
"@braintree/sanitize-url": "^3.0.0",
|
||||
"@kyleshockey/object-assign-deep": "^0.4.2",
|
||||
"@kyleshockey/xml": "^1.0.2",
|
||||
"base64-js": "^1.2.0",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^2.5.1",
|
||||
"css.escape": "1.5.1",
|
||||
"deep-extend": "0.6.0",
|
||||
"dompurify": "^2.0.7",
|
||||
"ieee754": "^1.1.13",
|
||||
"immutable": "^3.x.x",
|
||||
"js-file-download": "^0.4.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.15",
|
||||
"memoizee": "^0.4.12",
|
||||
"prop-types": "^15.7.2",
|
||||
"randombytes": "^2.1.0",
|
||||
"react": "^15.6.2",
|
||||
"react-debounce-input": "^3.2.0",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-immutable-proptypes": "2.1.0",
|
||||
"react-immutable-pure-component": "^1.1.1",
|
||||
"react-inspector": "^2.3.0",
|
||||
"react-motion": "^0.5.2",
|
||||
"react-redux": "^4.x.x",
|
||||
"redux": "^3.x.x",
|
||||
"redux-immutable": "3.1.0",
|
||||
"remarkable": "^1.7.4",
|
||||
"reselect": "^2.5.4",
|
||||
"serialize-error": "^2.1.0",
|
||||
"sha.js": "^2.4.11",
|
||||
"swagger-client": "^3.9.6",
|
||||
"url-parse": "^1.4.7",
|
||||
"xml-but-prettier": "^1.0.1",
|
||||
"zenscroll": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.2.0",
|
||||
"@babel/plugin-transform-runtime": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/register": "^7.4.4",
|
||||
"@release-it/conventional-changelog": "^1.1.0",
|
||||
"autoprefixer": "^8.4.1",
|
||||
"babel-eslint": "^9.0.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-module-resolver": "^3.2.0",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.13",
|
||||
"body-parser": "^1.19.0",
|
||||
"bundlesize": "^0.18.0",
|
||||
"chromedriver": "^2.38.3",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"cors": "^2.8.5",
|
||||
"css-loader": "^3.1.0",
|
||||
"cypress": "^3.4.1",
|
||||
"dedent": "^0.7.0",
|
||||
"deepmerge": "^2.1.0",
|
||||
"enzyme": "^2.7.1",
|
||||
"eslint": "^4.1.1",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-react": "^7.14.3",
|
||||
"expect": "^1.20.2",
|
||||
"express": "^4.17.1",
|
||||
"file-loader": "^4.1.0",
|
||||
"git-describe": "^4.0.4",
|
||||
"http-server": "^0.11.1",
|
||||
"ignore-assets-webpack-plugin": "^2.0.1",
|
||||
"imports-loader": "^0.8.0",
|
||||
"jsdom": "^11.10.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"json-merger": "^1.1.1",
|
||||
"json-server": "^0.15.0",
|
||||
"less": "^3.9.0",
|
||||
"license-checker": "^19.0.0",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"mocha": "^5.1.1",
|
||||
"nightwatch": "^1.1.13",
|
||||
"node-sass": "^4.12.0",
|
||||
"npm-audit-ci-wrapper": "^2.3.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"oauth2-server": "^2.4.1",
|
||||
"open": "^6.4.0",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "^1.18.2",
|
||||
"raw-loader": "^3.1.0",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
"release-it": "^12.3.4",
|
||||
"rimraf": "^2.6.3",
|
||||
"sass-loader": "^7.1.0",
|
||||
"selenium-server-standalone-jar": "^3.141.5",
|
||||
"source-map-support": "^0.5.12",
|
||||
"standard": "^11.0.1",
|
||||
"tachyons-sass": "^4.9.5",
|
||||
"terser-webpack-plugin": "^1.4.1",
|
||||
"url-loader": "^2.1.0",
|
||||
"webpack": "^4.39.0",
|
||||
"webpack-bundle-size-analyzer": "^2.5.0",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-dev-server": "^3.7.2"
|
||||
},
|
||||
"config": {
|
||||
"deps_check_dir": ".deps_check"
|
||||
},
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/swagger-ui-bundle.js",
|
||||
"maxSize": "1 MB",
|
||||
"compression": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -59,16 +59,15 @@ PERMISSIONS = {
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "mailbox", "view_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
["admin", "alias", "view_alias"],
|
||||
["admin", "senderaddress", "add_senderaddress"],
|
||||
["admin", "senderaddress", "change_senderaddress"],
|
||||
["admin", "senderaddress", "delete_senderaddress"],
|
||||
@@ -77,23 +76,21 @@ PERMISSIONS = {
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "mailbox", "view_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
["admin", "alias", "view_alias"],
|
||||
["admin", "senderaddress", "add_senderaddress"],
|
||||
["admin", "senderaddress", "change_senderaddress"],
|
||||
["admin", "senderaddress", "delete_senderaddress"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "add_domain"],
|
||||
["admin", "domain", "change_domain"],
|
||||
["admin", "domain", "delete_domain"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "domainalias", "add_domainalias"],
|
||||
["admin", "domainalias", "change_domainalias"],
|
||||
["admin", "domainalias", "delete_domainalias"],
|
||||
|
||||
@@ -15,4 +15,5 @@ BOWER_INSTALLED_APPS = (
|
||||
"font-awesome#4.7.0",
|
||||
"c3#0.4.10",
|
||||
"selectize#0.12.4",
|
||||
"swagger-ui#3.24.3",
|
||||
)
|
||||
|
||||
@@ -103,17 +103,15 @@ class ExtensionsPool(object):
|
||||
result = []
|
||||
for ext_name in list(self.extensions.keys()):
|
||||
ext = self.get_extension(ext_name)
|
||||
options = {}
|
||||
if category == "api":
|
||||
root = ""
|
||||
pattern = "{}.urls_api"
|
||||
else:
|
||||
root = r"^{}/".format(ext.get_url())
|
||||
options.update({"namespace": ext_name})
|
||||
pattern = "{}.urls"
|
||||
try:
|
||||
result.append(
|
||||
url(root, include(pattern.format(ext_name), **options))
|
||||
url(root, include(pattern.format(ext_name)))
|
||||
)
|
||||
except ImportError:
|
||||
# No urls for this extension
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.0.13 on 2020-01-04 17:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0017_auto_20190705_1045'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='last_name',
|
||||
field=models.CharField(blank=True, max_length=150, verbose_name='last name'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
|
||||
class OpenAPITestCase(ModoTestCase):
|
||||
openapi_schema_url = reverse_lazy('openapi_schema')
|
||||
|
||||
def test_unauthorized(self):
|
||||
self.client.logout()
|
||||
|
||||
response = self.client.get("/docs/api/")
|
||||
self.assertRedirects(response, "/accounts/login/?next=/docs/api/")
|
||||
|
||||
response = self.client.get(self.openapi_schema_url)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_get_schema(self):
|
||||
self.assertEqual(self.openapi_schema_url, "/docs/openapi.json")
|
||||
|
||||
response = self.client.get(self.openapi_schema_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json()['info'], {
|
||||
'title': "Modoboa API",
|
||||
'version': "1.0.0",
|
||||
})
|
||||
|
||||
def test_get_swagger_ui(self):
|
||||
response = self.client.get("/docs/api/")
|
||||
self.assertContains(response, "Modoboa API")
|
||||
@@ -122,7 +122,7 @@ class DashboardTestCase(ModoTestCase):
|
||||
self.client.logout()
|
||||
self.client.login(username=self.user.username, password="toto")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_custom_news(self):
|
||||
"""Check that custom news are displayed."""
|
||||
|
||||
+20
-18
@@ -1,28 +1,30 @@
|
||||
"""Core urls."""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "core"
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.RootDispatchView.as_view(), name="root"),
|
||||
url(r'^dashboard/$', views.DashboardView.as_view(), name="dashboard"),
|
||||
path('', views.RootDispatchView.as_view(), name="root"),
|
||||
path('dashboard/', views.DashboardView.as_view(), name="dashboard"),
|
||||
|
||||
url(r'^accounts/login/$', views.dologin, name="login"),
|
||||
url(r'^accounts/logout/$', views.dologout, name="logout"),
|
||||
path('accounts/login/', views.dologin, name="login"),
|
||||
path('accounts/logout/', views.dologout, name="logout"),
|
||||
|
||||
url(r'^core/$', views.viewsettings, name="index"),
|
||||
url(r'^core/parameters/$', views.parameters, name="parameters"),
|
||||
url(r'^core/info/$', views.information, name="information"),
|
||||
url(r'^core/logs/$', views.logs, name="log_list"),
|
||||
url(r'^core/logs/page/$', views.logs_page, name="logs_page"),
|
||||
url(r'^core/top_notifications/check/$',
|
||||
views.check_top_notifications,
|
||||
name="top_notifications_check"),
|
||||
path('core/', views.viewsettings, name="index"),
|
||||
path('core/parameters/', views.parameters, name="parameters"),
|
||||
path('core/info/', views.information, name="information"),
|
||||
path('core/logs/', views.logs, name="log_list"),
|
||||
path('core/logs/page/', views.logs_page, name="logs_page"),
|
||||
path('core/top_notifications/check/',
|
||||
views.check_top_notifications,
|
||||
name="top_notifications_check"),
|
||||
|
||||
url(r'^user/$', views.index, name="user_index"),
|
||||
url(r'^user/preferences/$', views.preferences,
|
||||
name="user_preferences"),
|
||||
url(r'^user/profile/$', views.profile, name="user_profile"),
|
||||
url(r'^user/api/$', views.api_access, name="user_api_access"),
|
||||
path('user/', views.index, name="user_index"),
|
||||
path('user/preferences/', views.preferences,
|
||||
name="user_preferences"),
|
||||
path('user/profile/', views.profile, name="user_profile"),
|
||||
path('user/api/', views.api_access, name="user_api_access"),
|
||||
]
|
||||
|
||||
@@ -4,13 +4,14 @@ from .admin import (
|
||||
check_top_notifications, information, logs, logs_page, parameters,
|
||||
viewsettings
|
||||
)
|
||||
from .auth import dologin, dologout, password_reset
|
||||
from .auth import PasswordResetView, dologin, dologout
|
||||
from .base import RootDispatchView
|
||||
from .dashboard import DashboardView
|
||||
from .user import api_access, index, preferences, profile
|
||||
|
||||
__all__ = [
|
||||
"DashboardView",
|
||||
"PasswordResetView",
|
||||
"RootDispatchView",
|
||||
"api_access",
|
||||
"check_top_notifications",
|
||||
@@ -21,7 +22,6 @@ __all__ = [
|
||||
"logs",
|
||||
"logs_page",
|
||||
"parameters",
|
||||
"password_reset",
|
||||
"preferences",
|
||||
"profile",
|
||||
"viewsettings",
|
||||
|
||||
@@ -115,11 +115,13 @@ def dologout(request):
|
||||
return HttpResponseRedirect(reverse("core:login"))
|
||||
|
||||
|
||||
def password_reset(request, **kwargs):
|
||||
class PasswordResetView(auth_views.PasswordResetView):
|
||||
"""Custom view to override form."""
|
||||
kwargs.update({
|
||||
"from_email": (
|
||||
request.localconfig.parameters.get_value("sender_address")),
|
||||
"password_reset_form": forms.PasswordResetForm
|
||||
})
|
||||
return auth_views.password_reset(request, **kwargs)
|
||||
|
||||
form_class = forms.PasswordResetForm
|
||||
|
||||
def setup(self, request, *args, **kwargs):
|
||||
super().setup(request, *args, **kwargs)
|
||||
self.from_email = request.localconfig.parameters.get_value(
|
||||
"sender_address"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ def find_nextlocation(request, user):
|
||||
nextlocation = request.POST.get("next", request.GET.get("next"))
|
||||
condition = (
|
||||
nextlocation and
|
||||
is_safe_url(nextlocation, host=request.get_host())
|
||||
is_safe_url(nextlocation, request.get_host())
|
||||
)
|
||||
if condition:
|
||||
return nextlocation
|
||||
|
||||
@@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class DnstoolsConfig(AppConfig):
|
||||
name = 'dnstools'
|
||||
name = 'modoboa.dnstools'
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
"""App related urls."""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "dnstools"
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^dnsrecords/(?P<pk>\d+)/$', views.DNSRecordDetailView.as_view(),
|
||||
name="dns_record_detail"),
|
||||
url(r'^dnsrecords/autoconfig/(?P<pk>\d+)/$',
|
||||
views.AutoConfigRecordsStatusView.as_view(),
|
||||
name="autoconfig_records_status"),
|
||||
url(r'^configuration/(?P<pk>\d+)/$',
|
||||
views.DomainDNSConfigurationView.as_view(),
|
||||
name="domain_dns_configuration"),
|
||||
path('dnsrecords/<int:pk>/', views.DNSRecordDetailView.as_view(),
|
||||
name="dns_record_detail"),
|
||||
path('dnsrecords/autoconfig/<int:pk>/',
|
||||
views.AutoConfigRecordsStatusView.as_view(),
|
||||
name="autoconfig_records_status"),
|
||||
path('configuration/<int:pk>/',
|
||||
views.DomainDNSConfigurationView.as_view(),
|
||||
name="domain_dns_configuration"),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Generated by Django 2.0.13 on 2020-01-04 17:21
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
import modoboa.limits.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [
|
||||
('limits', '0001_initial'),
|
||||
('limits', '0002_auto_20151114_1518'),
|
||||
('limits', '0003_auto_20160413_1046'),
|
||||
('limits', '0004_auto_20160413_1312'),
|
||||
]
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('admin', '0001_initial'),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserObjectLimit',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=254)),
|
||||
('max_value', models.IntegerField(default=0)),
|
||||
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='userobjectlimit',
|
||||
unique_together=set([('user', 'name')]),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DomainObjectLimit',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=254)),
|
||||
('max_value', models.IntegerField(default=0)),
|
||||
('domain', models.ForeignKey(to='admin.Domain', on_delete=models.CASCADE)),
|
||||
],
|
||||
bases=(modoboa.limits.models.ObjectLimitMixin, models.Model),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='domainobjectlimit',
|
||||
unique_together=set([('domain', 'name')]),
|
||||
),
|
||||
]
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.db import migrations, models
|
||||
import modoboa.limits.models
|
||||
from django.db import migrations
|
||||
|
||||
from .. import utils
|
||||
|
||||
@@ -24,24 +23,5 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DomainObjectLimit',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=254)),
|
||||
('max_value', models.IntegerField(default=0)),
|
||||
('domain', models.ForeignKey(to='admin.Domain', on_delete=models.CASCADE)),
|
||||
],
|
||||
bases=(modoboa.limits.models.ObjectLimitMixin, models.Model),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userobjectlimit',
|
||||
name='max_value',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='domainobjectlimit',
|
||||
unique_together=set([('domain', 'name')]),
|
||||
),
|
||||
migrations.RunPython(create_limits)
|
||||
]
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
|
||||
{% if wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="radio-inline">{% endif %}{% include "django/forms/widgets/input.html" %}{% if wrap_label %} {{ widget.label }}</label>{% endif %}
|
||||
{% if widget.wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="radio-inline">{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}</label>{% endif %}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Modoboa API</title>
|
||||
<link rel="stylesheet" href="{% static 'swagger-ui/dist/swagger-ui.css' %}">
|
||||
<style>
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.swagger-ui .highlight-code .download-contents {
|
||||
right: 20px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="{% static 'swagger-ui/dist/swagger-ui-bundle.js' %}"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "{% url 'openapi_schema' %}",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis
|
||||
],
|
||||
layout: "BaseLayout"
|
||||
});
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+37
-25
@@ -3,39 +3,43 @@ from functools import reduce
|
||||
from ckeditor_uploader import views as cku_views
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import include, path
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.i18n import JavaScriptCatalog
|
||||
|
||||
from rest_framework.documentation import include_docs_urls
|
||||
from rest_framework.renderers import JSONOpenAPIRenderer
|
||||
from rest_framework.schemas import get_schema_view
|
||||
|
||||
from modoboa.admin.views.user import forward
|
||||
from modoboa.admin.views import user as user_views
|
||||
from modoboa.core import signals as core_signals, views as core_views
|
||||
from modoboa.core.extensions import exts_pool
|
||||
|
||||
API_TITLE = "Modoboa API"
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name="javascript-catalog"),
|
||||
url(r'^ckeditor/upload/', login_required(cku_views.upload),
|
||||
name="ckeditor_upload"),
|
||||
url(r'^ckeditor/browse/', login_required(cku_views.browse),
|
||||
name="ckeditor_browse"),
|
||||
url("", include("modoboa.core.urls", namespace="core")),
|
||||
url("^user/forward/", forward, name="user_forward"),
|
||||
url("admin/", include("modoboa.admin.urls", namespace="admin")),
|
||||
url("dnstools/", include("modoboa.dnstools.urls", namespace="dnstools")),
|
||||
# No namespace
|
||||
url(r'^accounts/password_reset/$', core_views.password_reset,
|
||||
name="password_reset"),
|
||||
url(r'^accounts/password_reset/done/$', auth_views.password_reset_done,
|
||||
name="password_reset_done"),
|
||||
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', # noqa
|
||||
auth_views.password_reset_confirm, name="password_reset_confirm"),
|
||||
url(r'^reset/done/$', auth_views.password_reset_complete,
|
||||
name="password_reset_complete"),
|
||||
path('jsi18n/', JavaScriptCatalog.as_view(), name="javascript-catalog"),
|
||||
|
||||
path('ckeditor/upload/', login_required(cku_views.upload),
|
||||
name="ckeditor_upload"),
|
||||
path('ckeditor/browse/', login_required(cku_views.browse),
|
||||
name="ckeditor_browse"),
|
||||
|
||||
path('', include("modoboa.core.urls")),
|
||||
path('admin/', include("modoboa.admin.urls")),
|
||||
path('dnstools/', include("modoboa.dnstools.urls")),
|
||||
|
||||
path('user/forward/', user_views.forward, name="user_forward"),
|
||||
|
||||
path('accounts/password_reset/', core_views.PasswordResetView.as_view(),
|
||||
name="password_reset"),
|
||||
path('accounts/password_reset/done/',
|
||||
auth_views.PasswordResetDoneView.as_view(),
|
||||
name="password_reset_done"),
|
||||
path('reset/<uidb64>/<token>/',
|
||||
auth_views.PasswordResetConfirmView.as_view(),
|
||||
name="password_reset_confirm"),
|
||||
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(),
|
||||
name="password_reset_complete"),
|
||||
]
|
||||
|
||||
exts_pool.load_all()
|
||||
@@ -48,9 +52,17 @@ if extra_routes:
|
||||
urlpatterns += extra_routes
|
||||
|
||||
# API urls
|
||||
schema_view = get_schema_view(
|
||||
title="Modoboa API",
|
||||
version="1.0.0",
|
||||
public=False,
|
||||
renderer_classes=[JSONOpenAPIRenderer],
|
||||
)
|
||||
urlpatterns += [
|
||||
url("^docs/api/", include_docs_urls(title=API_TITLE, public=False)),
|
||||
url("^api/v1/", include("modoboa.urls_api", namespace="api")),
|
||||
path('docs/openapi.json', schema_view, name="openapi_schema"),
|
||||
path('docs/api/', login_required(
|
||||
TemplateView.as_view(template_name="swagger-ui.html"))),
|
||||
path('api/v1/', include("modoboa.urls_api")),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
||||
+7
-6
@@ -1,12 +1,13 @@
|
||||
"""External API urls."""
|
||||
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
|
||||
from modoboa.core.extensions import exts_pool
|
||||
|
||||
app_name = "api"
|
||||
|
||||
urlpatterns = [
|
||||
url("", include("modoboa.admin.urls_api")),
|
||||
url("", include("modoboa.limits.urls_api")),
|
||||
url("", include("modoboa.relaydomains.urls_api")),
|
||||
]
|
||||
urlpatterns += exts_pool.get_urls(category="api")
|
||||
path('', include("modoboa.admin.urls_api")),
|
||||
path('', include("modoboa.limits.urls_api")),
|
||||
path('', include("modoboa.relaydomains.urls_api")),
|
||||
] + exts_pool.get_urls(category="api")
|
||||
|
||||
+3
-5
@@ -1,15 +1,13 @@
|
||||
django>=1.11.8,<=1.11.99
|
||||
django>=2.2,<2.3
|
||||
django-braces==1.14.0
|
||||
django-ckeditor==5.8.0
|
||||
django-reversion==3.0.5
|
||||
django-subcommand2==0.1.1
|
||||
django-xforwardedfor-middleware==2.0
|
||||
|
||||
dj-database-url
|
||||
|
||||
coreapi==2.3.3
|
||||
coreapi-cli==1.0.9
|
||||
djangorestframework>=3.9,<3.10
|
||||
djangorestframework>=3.11,<3.12
|
||||
uritemplate
|
||||
|
||||
passlib==1.7.2
|
||||
bcrypt # Requires libffi-dev and python-dev
|
||||
|
||||
Reference in New Issue
Block a user