mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-23 20:03:11 +00:00
Merge branch 'clean_api_duplicate' into 'master'
Refactor API Closes #128 See merge request federez/re2o!172
This commit is contained in:
commit
2f8ea80e7f
26 changed files with 4266 additions and 1158 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -38,7 +38,7 @@ mkdir -p media/images
|
||||||
## MR 163: Fix install re2o
|
## MR 163: Fix install re2o
|
||||||
|
|
||||||
Refactored install_re2o.sh script.
|
Refactored install_re2o.sh script.
|
||||||
* There are more tools available with it but some fucntion have changed, report to [the dedicated wiki page](for more informations) or run:
|
* There are more tools available with it but some function have changed, report to [the dedicated wiki page](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Setup%20script)for more informations or run:
|
||||||
```
|
```
|
||||||
install_re2o.sh help
|
install_re2o.sh help
|
||||||
```
|
```
|
||||||
|
@ -53,3 +53,22 @@ Add the logo and fix somme issues on the navbar and home page. Only collecting t
|
||||||
python3 manage.py collectstatic
|
python3 manage.py collectstatic
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## MR 172: Refactor API
|
||||||
|
|
||||||
|
Creates a new (nearly) REST API to expose all models of Re2o. See [the dedicated wiki page](https://gitlab.federez.net/federez/re2o/wikis/API/Raw-Usage) for more details on how to use it.
|
||||||
|
* For testing purpose, add `volatildap` package:
|
||||||
|
```
|
||||||
|
pip3 install volatildap
|
||||||
|
```
|
||||||
|
* Activate HTTP Authorization passthrough in by adding the following in `/etc/apache2/site-available/re2o.conf` (example in `install_utils/apache2/re2o.conf`):
|
||||||
|
```
|
||||||
|
WSGIPassAuthorization On
|
||||||
|
```
|
||||||
|
* Activate the API if you want to use it by adding `'api'` to the optional apps in `re2o/settings_local.py`:
|
||||||
|
```
|
||||||
|
OPTIONAL_APPS = (
|
||||||
|
...
|
||||||
|
'api',
|
||||||
|
...
|
||||||
|
)
|
73
api/acl.py
Normal file
73
api/acl.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the ACL for the whole API.
|
||||||
|
|
||||||
|
Importing this module, creates the 'can view api' permission if not already
|
||||||
|
done.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
def _create_api_permission():
|
||||||
|
"""Creates the 'use_api' permission if not created.
|
||||||
|
|
||||||
|
The 'use_api' is a fake permission in the sense it is not associated with an
|
||||||
|
existing model and this ensure the permission is created every time this file
|
||||||
|
is imported.
|
||||||
|
"""
|
||||||
|
api_content_type, created = ContentType.objects.get_or_create(
|
||||||
|
app_label=settings.API_CONTENT_TYPE_APP_LABEL,
|
||||||
|
model=settings.API_CONTENT_TYPE_MODEL
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
api_content_type.save()
|
||||||
|
api_permission, created = Permission.objects.get_or_create(
|
||||||
|
name=settings.API_PERMISSION_NAME,
|
||||||
|
content_type=api_content_type,
|
||||||
|
codename=settings.API_PERMISSION_CODENAME
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
api_permission.save()
|
||||||
|
|
||||||
|
|
||||||
|
_create_api_permission()
|
||||||
|
|
||||||
|
|
||||||
|
def can_view(user):
|
||||||
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user: The user who wants to view the application.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A couple (allowed, msg) where allowed is a boolean which is True if
|
||||||
|
viewing is granted and msg is a message (can be None).
|
||||||
|
"""
|
||||||
|
kwargs = {
|
||||||
|
'app_label': settings.API_CONTENT_TYPE_APP_LABEL,
|
||||||
|
'codename': settings.API_PERMISSION_CODENAME
|
||||||
|
}
|
||||||
|
can = user.has_perm('%(app_label)s.%(codename)s' % kwargs)
|
||||||
|
return can, None if can else _("You cannot see this application.")
|
48
api/authentication.py
Normal file
48
api/authentication.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the authentication classes used in the API to authenticate a user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework.authentication import TokenAuthentication
|
||||||
|
from rest_framework import exceptions
|
||||||
|
|
||||||
|
class ExpiringTokenAuthentication(TokenAuthentication):
|
||||||
|
"""Authenticate a user if the provided token is valid and not expired.
|
||||||
|
"""
|
||||||
|
def authenticate_credentials(self, key):
|
||||||
|
"""See base class. Add the verification the token is not expired.
|
||||||
|
"""
|
||||||
|
base = super(ExpiringTokenAuthentication, self)
|
||||||
|
user, token = base.authenticate_credentials(key)
|
||||||
|
|
||||||
|
# Check that the genration time of the token is not too old
|
||||||
|
token_duration = datetime.timedelta(
|
||||||
|
seconds=settings.API_TOKEN_DURATION
|
||||||
|
)
|
||||||
|
utc_now = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
if token.created < utc_now - token_duration:
|
||||||
|
raise exceptions.AuthenticationFailed(_('Token has expired'))
|
||||||
|
|
||||||
|
return (token.user, token)
|
62
api/pagination.py
Normal file
62
api/pagination.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the pagination classes used in the API to paginate the results.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rest_framework import pagination
|
||||||
|
|
||||||
|
|
||||||
|
class PageSizedPagination(pagination.PageNumberPagination):
|
||||||
|
"""Provide the possibility to control the page size by using the
|
||||||
|
'page_size' parameter. The value 'all' can be used for this parameter
|
||||||
|
to retrieve all the results in a single page.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
page_size_query_param: The string to look for in the parameters of
|
||||||
|
a query to get the page_size requested.
|
||||||
|
all_pages_strings: A set of strings that can be used in the query to
|
||||||
|
request all results in a single page.
|
||||||
|
max_page_size: The maximum number of results a page can output no
|
||||||
|
matter what is requested.
|
||||||
|
"""
|
||||||
|
page_size_query_param = 'page_size'
|
||||||
|
all_pages_strings = ('all',)
|
||||||
|
max_page_size = 10000
|
||||||
|
|
||||||
|
def get_page_size(self, request):
|
||||||
|
"""Retrieve the size of the page according to the parameters of the
|
||||||
|
request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: the request of the user
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A integer between 0 and `max_page_size` that represent the size
|
||||||
|
of the page to use.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
page_size_str = request.query_params[self.page_size_query_param]
|
||||||
|
if page_size_str in self.all_pages_strings:
|
||||||
|
return self.max_page_size
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return super(PageSizedPagination, self).get_page_size(request)
|
284
api/permissions.py
Normal file
284
api/permissions.py
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the permission classes used in the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rest_framework import permissions, exceptions
|
||||||
|
|
||||||
|
from re2o.acl import can_create, can_edit, can_delete, can_view_all
|
||||||
|
|
||||||
|
from . import acl
|
||||||
|
|
||||||
|
|
||||||
|
def can_see_api(*_, **__):
|
||||||
|
"""Check if a user can view the API.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A function that takes a user as an argument and returns
|
||||||
|
an ACL tuple that assert this user can see the API.
|
||||||
|
"""
|
||||||
|
return lambda user: acl.can_view(user)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_param_in_view(view, param_name):
|
||||||
|
"""Utility function to retrieve an attribute in a view passed in argument.
|
||||||
|
|
||||||
|
Uses the result of `{view}.get_{param_name}()` if existing else uses the
|
||||||
|
value of `{view}.{param_name}` directly.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
view: The view where to look into.
|
||||||
|
param_name: The name of the attribute to look for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The result of the getter function if found else the value of the
|
||||||
|
attribute itself.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: None of the getter function or the attribute are
|
||||||
|
defined in the view.
|
||||||
|
"""
|
||||||
|
assert hasattr(view, 'get_'+param_name) \
|
||||||
|
or getattr(view, param_name, None) is not None, (
|
||||||
|
'cannot apply {} on a view that does not set '
|
||||||
|
'`.{}` or have a `.get_{}()` method.'
|
||||||
|
).format(self.__class__.__name__, param_name, param_name)
|
||||||
|
|
||||||
|
if hasattr(view, 'get_'+param_name):
|
||||||
|
param = getattr(view, 'get_'+param_name)()
|
||||||
|
assert param is not None, (
|
||||||
|
'{}.get_{}() returned None'
|
||||||
|
).format(view.__class__.__name__, param_name)
|
||||||
|
return param
|
||||||
|
return getattr(view, param_name)
|
||||||
|
|
||||||
|
|
||||||
|
class ACLPermission(permissions.BasePermission):
|
||||||
|
"""A permission class used to check the ACL to validate the permissions
|
||||||
|
of a user.
|
||||||
|
|
||||||
|
The view must define a `.get_perms_map()` or a `.perms_map` attribute.
|
||||||
|
See the wiki for the syntax of this attribute.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_required_permissions(self, method, view):
|
||||||
|
"""Build the list of permissions required for the request to be
|
||||||
|
accepted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
method: The HTTP method name used for the request.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The list of ACL functions to apply to a user in order to check
|
||||||
|
if he has the right permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: None of `.get_perms_map()` or `.perms_map` are
|
||||||
|
defined in the view.
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
perms_map = _get_param_in_view(view, 'perms_map')
|
||||||
|
|
||||||
|
if method not in perms_map:
|
||||||
|
raise exceptions.MethodNotAllowed(method)
|
||||||
|
|
||||||
|
return [can_see_api()] + list(perms_map[method])
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
"""Check that the user has the permissions to perform the request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The request performed.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean indicating if the user has the permission to
|
||||||
|
perform the request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: None of `.get_perms_map()` or `.perms_map` are
|
||||||
|
defined in the view.
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
# Workaround to ensure ACLPermissions are not applied
|
||||||
|
# to the root view when using DefaultRouter.
|
||||||
|
if getattr(view, '_ignore_model_permissions', False):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not request.user or not request.user.is_authenticated:
|
||||||
|
return False
|
||||||
|
|
||||||
|
perms = self.get_required_permissions(request.method, view)
|
||||||
|
|
||||||
|
return all(perm(request.user)[0] for perm in perms)
|
||||||
|
|
||||||
|
|
||||||
|
class AutodetectACLPermission(permissions.BasePermission):
|
||||||
|
"""A permission class used to autodetect the ACL needed to validate the
|
||||||
|
permissions of a user based on the queryset of the view.
|
||||||
|
|
||||||
|
The view must define a `.get_queryset()` or a `.queryset` attribute.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
perms_map: The mapping of each valid HTTP method to the required
|
||||||
|
model-based ACL permissions.
|
||||||
|
perms_obj_map: The mapping of each valid HTTP method to the required
|
||||||
|
object-based ACL permissions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
perms_map = {
|
||||||
|
'GET': [can_see_api, lambda model: model.can_view_all],
|
||||||
|
'OPTIONS': [can_see_api, lambda model: model.can_view_all],
|
||||||
|
'HEAD': [can_see_api, lambda model: model.can_view_all],
|
||||||
|
'POST': [can_see_api, lambda model: model.can_create],
|
||||||
|
'PUT': [], # No restrictions, apply to objects
|
||||||
|
'PATCH': [], # No restrictions, apply to objects
|
||||||
|
'DELETE': [], # No restrictions, apply to objects
|
||||||
|
}
|
||||||
|
perms_obj_map = {
|
||||||
|
'GET': [can_see_api, lambda obj: obj.can_view],
|
||||||
|
'OPTIONS': [can_see_api, lambda obj: obj.can_view],
|
||||||
|
'HEAD': [can_see_api, lambda obj: obj.can_view],
|
||||||
|
'POST': [], # No restrictions, apply to models
|
||||||
|
'PUT': [can_see_api, lambda obj: obj.can_edit],
|
||||||
|
'PATCH': [can_see_api, lambda obj: obj.can_edit],
|
||||||
|
'DELETE': [can_see_api, lambda obj: obj.can_delete],
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_required_permissions(self, method, model):
|
||||||
|
"""Build the list of model-based permissions required for the
|
||||||
|
request to be accepted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
method: The HTTP method name used for the request.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The list of ACL functions to apply to a user in order to check
|
||||||
|
if he has the right permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
if method not in self.perms_map:
|
||||||
|
raise exceptions.MethodNotAllowed(method)
|
||||||
|
|
||||||
|
return [perm(model) for perm in self.perms_map[method]]
|
||||||
|
|
||||||
|
def get_required_object_permissions(self, method, obj):
|
||||||
|
"""Build the list of object-based permissions required for the
|
||||||
|
request to be accepted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
method: The HTTP method name used for the request.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The list of ACL functions to apply to a user in order to check
|
||||||
|
if he has the right permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
if method not in self.perms_obj_map:
|
||||||
|
raise exceptions.MethodNotAllowed(method)
|
||||||
|
|
||||||
|
return [perm(obj) for perm in self.perms_obj_map[method]]
|
||||||
|
|
||||||
|
def _queryset(self, view):
|
||||||
|
return _get_param_in_view(view, 'queryset')
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
"""Check that the user has the model-based permissions to perform
|
||||||
|
the request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The request performed.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean indicating if the user has the permission to
|
||||||
|
perform the request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: None of `.get_queryset()` or `.queryset` are
|
||||||
|
defined in the view.
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
# Workaround to ensure ACLPermissions are not applied
|
||||||
|
# to the root view when using DefaultRouter.
|
||||||
|
if getattr(view, '_ignore_model_permissions', False):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not request.user or not request.user.is_authenticated:
|
||||||
|
return False
|
||||||
|
|
||||||
|
queryset = self._queryset(view)
|
||||||
|
perms = self.get_required_permissions(request.method, queryset.model)
|
||||||
|
|
||||||
|
return all(perm(request.user)[0] for perm in perms)
|
||||||
|
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
"""Check that the user has the object-based permissions to perform
|
||||||
|
the request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The request performed.
|
||||||
|
view: The view which is responding to the request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean indicating if the user has the permission to
|
||||||
|
perform the request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
rest_framework.exception.MethodNotAllowed: The requested method
|
||||||
|
is not allowed for this view.
|
||||||
|
"""
|
||||||
|
# authentication checks have already executed via has_permission
|
||||||
|
user = request.user
|
||||||
|
|
||||||
|
perms = self.get_required_object_permissions(request.method, obj)
|
||||||
|
|
||||||
|
if not all(perm(request.user)[0] for perm in perms):
|
||||||
|
# If the user does not have permissions we need to determine if
|
||||||
|
# they have read permissions to see 403, or not, and simply see
|
||||||
|
# a 404 response.
|
||||||
|
|
||||||
|
if request.method in SAFE_METHODS:
|
||||||
|
# Read permissions already checked and failed, no need
|
||||||
|
# to make another lookup.
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
read_perms = self.get_required_object_permissions('GET', obj)
|
||||||
|
if not read_perms(request.user)[0]:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
# Has read permissions.
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
157
api/routers.py
Normal file
157
api/routers.py
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Mael Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the custom routers to generate the URLs of the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from django.conf.urls import url, include
|
||||||
|
from django.core.urlresolvers import NoReverseMatch
|
||||||
|
from rest_framework import views
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.reverse import reverse
|
||||||
|
from rest_framework.schemas import SchemaGenerator
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
|
class AllViewsRouter(DefaultRouter):
|
||||||
|
"""A router that can register both viewsets and views and generates
|
||||||
|
a full API root page with all the generated URLs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.view_registry = []
|
||||||
|
super(AllViewsRouter, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def register_viewset(self, *args, **kwargs):
|
||||||
|
"""Register a viewset in the router. Alias of `register` for
|
||||||
|
convenience.
|
||||||
|
|
||||||
|
See `register` in the base class for details.
|
||||||
|
"""
|
||||||
|
return self.register(*args, **kwargs)
|
||||||
|
|
||||||
|
def register_view(self, pattern, view, name=None):
|
||||||
|
"""Register a view in the router.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: The URL pattern to use for this view.
|
||||||
|
view: The class-based view to register.
|
||||||
|
name: An optional name for the route generated. Defaults is
|
||||||
|
based on the pattern last section (delimited by '/').
|
||||||
|
"""
|
||||||
|
if name is None:
|
||||||
|
name = self.get_default_name(pattern)
|
||||||
|
self.view_registry.append((pattern, view, name))
|
||||||
|
|
||||||
|
def get_default_name(self, pattern):
|
||||||
|
"""Returns the name to use for the route if none was specified.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: The pattern for this route.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name to use for this route.
|
||||||
|
"""
|
||||||
|
return pattern.split('/')[-1]
|
||||||
|
|
||||||
|
def get_api_root_view(self, schema_urls=None):
|
||||||
|
"""Create a class-based view to use as the API root.
|
||||||
|
|
||||||
|
Highly inspired by the base class. See details on the implementation
|
||||||
|
in the base class. The only difference is that registered view URLs
|
||||||
|
are added after the registered viewset URLs on this root API page.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
schema_urls: A schema to use for the URLs.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The view to use to display the root API page.
|
||||||
|
"""
|
||||||
|
api_root_dict = OrderedDict()
|
||||||
|
list_name = self.routes[0].name
|
||||||
|
for prefix, viewset, basename in self.registry:
|
||||||
|
api_root_dict[prefix] = list_name.format(basename=basename)
|
||||||
|
for pattern, view, name in self.view_registry:
|
||||||
|
api_root_dict[pattern] = name
|
||||||
|
|
||||||
|
view_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
|
||||||
|
schema_media_types = []
|
||||||
|
|
||||||
|
if schema_urls and self.schema_title:
|
||||||
|
view_renderers += list(self.schema_renderers)
|
||||||
|
schema_generator = SchemaGenerator(
|
||||||
|
title=self.schema_title,
|
||||||
|
patterns=schema_urls
|
||||||
|
)
|
||||||
|
schema_media_types = [
|
||||||
|
renderer.media_type
|
||||||
|
for renderer in self.schema_renderers
|
||||||
|
]
|
||||||
|
|
||||||
|
class APIRoot(views.APIView):
|
||||||
|
_ignore_model_permissions = True
|
||||||
|
renderer_classes = view_renderers
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if request.accepted_renderer.media_type in schema_media_types:
|
||||||
|
# Return a schema response.
|
||||||
|
schema = schema_generator.get_schema(request)
|
||||||
|
if schema is None:
|
||||||
|
raise exceptions.PermissionDenied()
|
||||||
|
return Response(schema)
|
||||||
|
|
||||||
|
# Return a plain {"name": "hyperlink"} response.
|
||||||
|
ret = OrderedDict()
|
||||||
|
namespace = request.resolver_match.namespace
|
||||||
|
for key, url_name in api_root_dict.items():
|
||||||
|
if namespace:
|
||||||
|
url_name = namespace + ':' + url_name
|
||||||
|
try:
|
||||||
|
ret[key] = reverse(
|
||||||
|
url_name,
|
||||||
|
args=args,
|
||||||
|
kwargs=kwargs,
|
||||||
|
request=request,
|
||||||
|
format=kwargs.get('format', None)
|
||||||
|
)
|
||||||
|
except NoReverseMatch:
|
||||||
|
# Don't bail out if eg. no list routes exist, only detail routes.
|
||||||
|
continue
|
||||||
|
|
||||||
|
return Response(ret)
|
||||||
|
|
||||||
|
return APIRoot.as_view()
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
"""Builds the list of URLs to register.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of the URLs generated based on the viewsets registered
|
||||||
|
followed by the URLs generated based on the views registered.
|
||||||
|
"""
|
||||||
|
urls = super(AllViewsRouter, self).get_urls()
|
||||||
|
|
||||||
|
for pattern, view, name in self.view_registry:
|
||||||
|
urls.append(url(pattern, view.as_view(), name=name))
|
||||||
|
|
||||||
|
return urls
|
1102
api/serializers.py
1102
api/serializers.py
File diff suppressed because it is too large
Load diff
50
api/settings.py
Normal file
50
api/settings.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Settings specific to the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# RestFramework config for API
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'URL_FIELD_NAME': 'api_url',
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
'api.authentication.ExpiringTokenAuthentication',
|
||||||
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
|
),
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
|
'api.permissions.AutodetectACLPermission',
|
||||||
|
),
|
||||||
|
'DEFAULT_PAGINATION_CLASS': 'api.pagination.PageSizedPagination',
|
||||||
|
'PAGE_SIZE': 100
|
||||||
|
}
|
||||||
|
|
||||||
|
# API permission settings
|
||||||
|
API_CONTENT_TYPE_APP_LABEL = 'api'
|
||||||
|
API_CONTENT_TYPE_MODEL = 'api'
|
||||||
|
API_PERMISSION_NAME = 'Can use the API'
|
||||||
|
API_PERMISSION_CODENAME = 'use_api'
|
||||||
|
|
||||||
|
# Activate token authentication
|
||||||
|
API_APPS = (
|
||||||
|
'rest_framework.authtoken',
|
||||||
|
)
|
||||||
|
|
||||||
|
# The expiration time for an authentication token
|
||||||
|
API_TOKEN_DURATION = 86400 # 24 hours
|
764
api/tests.py
764
api/tests.py
|
@ -2,9 +2,7 @@
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
#
|
#
|
||||||
# Copyright © 2017 Gabriel Détraz
|
# Copyright © 2018 Maël Kervella
|
||||||
# Copyright © 2017 Goulven Kermarec
|
|
||||||
# Copyright © 2017 Augustin Lemesle
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,10 +17,762 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
"""api.tests
|
"""Defines the test suite for the API
|
||||||
The tests for the API module.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# from django.test import TestCase
|
import json
|
||||||
|
import datetime
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from requests import codes
|
||||||
|
|
||||||
|
import cotisations.models as cotisations
|
||||||
|
import machines.models as machines
|
||||||
|
import preferences.models as preferences
|
||||||
|
import topologie.models as topologie
|
||||||
|
import users.models as users
|
||||||
|
|
||||||
|
|
||||||
|
class APIEndpointsTestCase(APITestCase):
|
||||||
|
"""Test case to test that all endpoints are reachable with respects to
|
||||||
|
authentication and permission checks.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
no_auth_endpoints: A list of endpoints that should be reachable
|
||||||
|
without authentication.
|
||||||
|
auth_no_perm_endpoints: A list of endpoints that should be reachable
|
||||||
|
when being authenticated but without permissions.
|
||||||
|
auth_perm_endpoints: A list of endpoints that should be reachable
|
||||||
|
when being authenticated and having the correct permissions.
|
||||||
|
stduser: A standard user with no permission used for the tests and
|
||||||
|
initialized at the beggining of this test case.
|
||||||
|
superuser: A superuser (with all permissions) used for the tests and
|
||||||
|
initialized at the beggining of this test case.
|
||||||
|
"""
|
||||||
|
no_auth_endpoints = [
|
||||||
|
'/api/'
|
||||||
|
]
|
||||||
|
auth_no_perm_endpoints = []
|
||||||
|
auth_perm_endpoints = [
|
||||||
|
'/api/cotisations/article/',
|
||||||
|
'/api/cotisations/article/1/',
|
||||||
|
'/api/cotisations/banque/',
|
||||||
|
'/api/cotisations/banque/1/',
|
||||||
|
'/api/cotisations/cotisation/',
|
||||||
|
'/api/cotisations/cotisation/1/',
|
||||||
|
'/api/cotisations/facture/',
|
||||||
|
'/api/cotisations/facture/1/',
|
||||||
|
'/api/cotisations/paiement/',
|
||||||
|
'/api/cotisations/paiement/1/',
|
||||||
|
'/api/cotisations/vente/',
|
||||||
|
'/api/cotisations/vente/1/',
|
||||||
|
'/api/machines/domain/',
|
||||||
|
'/api/machines/domain/1/',
|
||||||
|
'/api/machines/extension/',
|
||||||
|
'/api/machines/extension/1/',
|
||||||
|
'/api/machines/interface/',
|
||||||
|
'/api/machines/interface/1/',
|
||||||
|
'/api/machines/iplist/',
|
||||||
|
'/api/machines/iplist/1/',
|
||||||
|
'/api/machines/iptype/',
|
||||||
|
'/api/machines/iptype/1/',
|
||||||
|
'/api/machines/ipv6list/',
|
||||||
|
'/api/machines/ipv6list/1/',
|
||||||
|
'/api/machines/machine/',
|
||||||
|
'/api/machines/machine/1/',
|
||||||
|
'/api/machines/machinetype/',
|
||||||
|
'/api/machines/machinetype/1/',
|
||||||
|
'/api/machines/mx/',
|
||||||
|
'/api/machines/mx/1/',
|
||||||
|
'/api/machines/nas/',
|
||||||
|
'/api/machines/nas/1/',
|
||||||
|
'/api/machines/ns/',
|
||||||
|
'/api/machines/ns/1/',
|
||||||
|
'/api/machines/ouvertureportlist/',
|
||||||
|
'/api/machines/ouvertureportlist/1/',
|
||||||
|
'/api/machines/ouvertureport/',
|
||||||
|
'/api/machines/ouvertureport/1/',
|
||||||
|
'/api/machines/servicelink/',
|
||||||
|
'/api/machines/servicelink/1/',
|
||||||
|
'/api/machines/service/',
|
||||||
|
'/api/machines/service/1/',
|
||||||
|
'/api/machines/soa/',
|
||||||
|
'/api/machines/soa/1/',
|
||||||
|
'/api/machines/srv/',
|
||||||
|
'/api/machines/srv/1/',
|
||||||
|
'/api/machines/txt/',
|
||||||
|
'/api/machines/txt/1/',
|
||||||
|
'/api/machines/vlan/',
|
||||||
|
'/api/machines/vlan/1/',
|
||||||
|
'/api/preferences/optionaluser/',
|
||||||
|
'/api/preferences/optionalmachine/',
|
||||||
|
'/api/preferences/optionaltopologie/',
|
||||||
|
'/api/preferences/generaloption/',
|
||||||
|
'/api/preferences/service/',
|
||||||
|
'/api/preferences/service/1/',
|
||||||
|
'/api/preferences/assooption/',
|
||||||
|
'/api/preferences/homeoption/',
|
||||||
|
'/api/preferences/mailmessageoption/',
|
||||||
|
'/api/topologie/acesspoint/',
|
||||||
|
# 2nd machine to be create (machines_machine_1, topologie_accesspoint_1)
|
||||||
|
'/api/topologie/acesspoint/2/',
|
||||||
|
'/api/topologie/building/',
|
||||||
|
'/api/topologie/building/1/',
|
||||||
|
'/api/topologie/constructorswitch/',
|
||||||
|
'/api/topologie/constructorswitch/1/',
|
||||||
|
'/api/topologie/modelswitch/',
|
||||||
|
'/api/topologie/modelswitch/1/',
|
||||||
|
'/api/topologie/room/',
|
||||||
|
'/api/topologie/room/1/',
|
||||||
|
'/api/topologie/server/',
|
||||||
|
# 3rd machine to be create (machines_machine_1, topologie_accesspoint_1,
|
||||||
|
# topologie_server_1)
|
||||||
|
'/api/topologie/server/3/',
|
||||||
|
'/api/topologie/stack/',
|
||||||
|
'/api/topologie/stack/1/',
|
||||||
|
'/api/topologie/switch/',
|
||||||
|
# 4th machine to be create (machines_machine_1, topologie_accesspoint_1,
|
||||||
|
# topologie_server_1, topologie_switch_1)
|
||||||
|
'/api/topologie/switch/4/',
|
||||||
|
'/api/topologie/switchbay/',
|
||||||
|
'/api/topologie/switchbay/1/',
|
||||||
|
'/api/topologie/switchport/',
|
||||||
|
'/api/topologie/switchport/1/',
|
||||||
|
'/api/topologie/switchport/2/',
|
||||||
|
'/api/topologie/switchport/3/',
|
||||||
|
'/api/users/adherent/',
|
||||||
|
# 3rd user to be create (stduser, superuser, users_adherent_1)
|
||||||
|
'/api/users/adherent/3/',
|
||||||
|
'/api/users/ban/',
|
||||||
|
'/api/users/ban/1/',
|
||||||
|
'/api/users/club/',
|
||||||
|
# 4th user to be create (stduser, superuser, users_adherent_1,
|
||||||
|
# users_club_1)
|
||||||
|
'/api/users/club/4/',
|
||||||
|
'/api/users/listright/',
|
||||||
|
# TODO: Merge !145
|
||||||
|
# '/api/users/listright/1/',
|
||||||
|
'/api/users/school/',
|
||||||
|
'/api/users/school/1/',
|
||||||
|
'/api/users/serviceuser/',
|
||||||
|
'/api/users/serviceuser/1/',
|
||||||
|
'/api/users/shell/',
|
||||||
|
'/api/users/shell/1/',
|
||||||
|
'/api/users/user/',
|
||||||
|
'/api/users/user/1/',
|
||||||
|
'/api/users/whitelist/',
|
||||||
|
'/api/users/whitelist/1/',
|
||||||
|
'/api/dns/zones/',
|
||||||
|
'/api/dhcp/hostmacip/',
|
||||||
|
'/api/mailing/standard',
|
||||||
|
'/api/mailing/club',
|
||||||
|
'/api/services/regen/',
|
||||||
|
]
|
||||||
|
not_found_endpoints = [
|
||||||
|
'/api/cotisations/article/4242/',
|
||||||
|
'/api/cotisations/banque/4242/',
|
||||||
|
'/api/cotisations/cotisation/4242/',
|
||||||
|
'/api/cotisations/facture/4242/',
|
||||||
|
'/api/cotisations/paiement/4242/',
|
||||||
|
'/api/cotisations/vente/4242/',
|
||||||
|
'/api/machines/domain/4242/',
|
||||||
|
'/api/machines/extension/4242/',
|
||||||
|
'/api/machines/interface/4242/',
|
||||||
|
'/api/machines/iplist/4242/',
|
||||||
|
'/api/machines/iptype/4242/',
|
||||||
|
'/api/machines/ipv6list/4242/',
|
||||||
|
'/api/machines/machine/4242/',
|
||||||
|
'/api/machines/machinetype/4242/',
|
||||||
|
'/api/machines/mx/4242/',
|
||||||
|
'/api/machines/nas/4242/',
|
||||||
|
'/api/machines/ns/4242/',
|
||||||
|
'/api/machines/ouvertureportlist/4242/',
|
||||||
|
'/api/machines/ouvertureport/4242/',
|
||||||
|
'/api/machines/servicelink/4242/',
|
||||||
|
'/api/machines/service/4242/',
|
||||||
|
'/api/machines/soa/4242/',
|
||||||
|
'/api/machines/srv/4242/',
|
||||||
|
'/api/machines/txt/4242/',
|
||||||
|
'/api/machines/vlan/4242/',
|
||||||
|
'/api/preferences/service/4242/',
|
||||||
|
'/api/topologie/acesspoint/4242/',
|
||||||
|
'/api/topologie/building/4242/',
|
||||||
|
'/api/topologie/constructorswitch/4242/',
|
||||||
|
'/api/topologie/modelswitch/4242/',
|
||||||
|
'/api/topologie/room/4242/',
|
||||||
|
'/api/topologie/server/4242/',
|
||||||
|
'/api/topologie/stack/4242/',
|
||||||
|
'/api/topologie/switch/4242/',
|
||||||
|
'/api/topologie/switchbay/4242/',
|
||||||
|
'/api/topologie/switchport/4242/',
|
||||||
|
'/api/users/adherent/4242/',
|
||||||
|
'/api/users/ban/4242/',
|
||||||
|
'/api/users/club/4242/',
|
||||||
|
'/api/users/listright/4242/',
|
||||||
|
'/api/users/school/4242/',
|
||||||
|
'/api/users/serviceuser/4242/',
|
||||||
|
'/api/users/shell/4242/',
|
||||||
|
'/api/users/user/4242/',
|
||||||
|
'/api/users/whitelist/4242/',
|
||||||
|
]
|
||||||
|
|
||||||
|
stduser = None
|
||||||
|
superuser = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
# Be aware that every object created here is never actually committed
|
||||||
|
# to the database. TestCase uses rollbacks after each test to cancel all
|
||||||
|
# modifications and recreates the data defined here before each test.
|
||||||
|
# For more details, see
|
||||||
|
# https://docs.djangoproject.com/en/1.10/topics/testing/tools/#testcase
|
||||||
|
|
||||||
|
super(APIEndpointsTestCase, cls).setUpClass()
|
||||||
|
|
||||||
|
# A user with no rights
|
||||||
|
cls.stduser = users.User.objects.create_user(
|
||||||
|
"apistduser",
|
||||||
|
"apistduser",
|
||||||
|
"apistduser@example.net",
|
||||||
|
"apistduser"
|
||||||
|
)
|
||||||
|
# A user with all the rights
|
||||||
|
cls.superuser = users.User.objects.create_superuser(
|
||||||
|
"apisuperuser",
|
||||||
|
"apisuperuser",
|
||||||
|
"apisuperuser@example.net",
|
||||||
|
"apisuperuser"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Creates 1 instance for each object so the "details" endpoints
|
||||||
|
# can be tested too. Objects need to be created in the right order.
|
||||||
|
# Dependencies (relatedFields, ...) are highlighted by a comment at
|
||||||
|
# the end of the concerned line (# Dep <model>).
|
||||||
|
cls.users_school_1 = users.School.objects.create(
|
||||||
|
name="users_school_1"
|
||||||
|
)
|
||||||
|
cls.users_school_1.save()
|
||||||
|
cls.users_listshell_1 = users.ListShell.objects.create(
|
||||||
|
shell="users_listshell_1"
|
||||||
|
)
|
||||||
|
cls.users_adherent_1 = users.Adherent.objects.create(
|
||||||
|
password="password",
|
||||||
|
last_login=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
surname="users_adherent_1",
|
||||||
|
pseudo="usersadherent1",
|
||||||
|
email="users_adherent_1@example.net",
|
||||||
|
school=cls.users_school_1, # Dep users.School
|
||||||
|
shell=cls.users_listshell_1, # Dep users.ListShell
|
||||||
|
comment="users Adherent 1 comment",
|
||||||
|
pwd_ntlm="",
|
||||||
|
state=users.User.STATES[0][0],
|
||||||
|
registered=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
telephone="0123456789",
|
||||||
|
uid_number=21102,
|
||||||
|
rezo_rez_uid=21102
|
||||||
|
)
|
||||||
|
cls.users_user_1 = cls.users_adherent_1
|
||||||
|
cls.cotisations_article_1 = cotisations.Article.objects.create(
|
||||||
|
name="cotisations_article_1",
|
||||||
|
prix=10,
|
||||||
|
duration=1,
|
||||||
|
type_user=cotisations.Article.USER_TYPES[0][0],
|
||||||
|
type_cotisation=cotisations.Article.COTISATION_TYPE[0][0]
|
||||||
|
)
|
||||||
|
cls.cotisations_banque_1 = cotisations.Banque.objects.create(
|
||||||
|
name="cotisations_banque_1"
|
||||||
|
)
|
||||||
|
cls.cotisations_paiement_1 = cotisations.Paiement.objects.create(
|
||||||
|
moyen="cotisations_paiement_1",
|
||||||
|
type_paiement=cotisations.Paiement.PAYMENT_TYPES[0][0]
|
||||||
|
)
|
||||||
|
cls.cotisations_facture_1 = cotisations.Facture.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
paiement=cls.cotisations_paiement_1, # Dep cotisations.Paiement
|
||||||
|
banque=cls.cotisations_banque_1, # Dep cotisations.Banque
|
||||||
|
cheque="1234567890",
|
||||||
|
date=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
valid=True,
|
||||||
|
control=False
|
||||||
|
)
|
||||||
|
cls.cotisations_vente_1 = cotisations.Vente.objects.create(
|
||||||
|
facture=cls.cotisations_facture_1, # Dep cotisations.Facture
|
||||||
|
number=2,
|
||||||
|
name="cotisations_vente_1",
|
||||||
|
prix=10,
|
||||||
|
duration=1,
|
||||||
|
type_cotisation=cotisations.Vente.COTISATION_TYPE[0][0]
|
||||||
|
)
|
||||||
|
# A cotisation is automatically created by the Vente object and
|
||||||
|
# trying to create another cotisation associated with this vente
|
||||||
|
# will fail so we simply retrieve it so it can be used in the tests
|
||||||
|
cls.cotisations_cotisation_1 = cotisations.Cotisation.objects.get(
|
||||||
|
vente=cls.cotisations_vente_1, # Dep cotisations.Vente
|
||||||
|
)
|
||||||
|
cls.machines_machine_1 = machines.Machine.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
name="machines_machine_1",
|
||||||
|
active=True
|
||||||
|
)
|
||||||
|
cls.machines_ouvertureportlist_1 = machines.OuverturePortList.objects.create(
|
||||||
|
name="machines_ouvertureportlist_1"
|
||||||
|
)
|
||||||
|
cls.machines_soa_1 = machines.SOA.objects.create(
|
||||||
|
name="machines_soa_1",
|
||||||
|
mail="postmaster@example.net",
|
||||||
|
refresh=86400,
|
||||||
|
retry=7200,
|
||||||
|
expire=3600000,
|
||||||
|
ttl=172800
|
||||||
|
)
|
||||||
|
cls.machines_extension_1 = machines.Extension.objects.create(
|
||||||
|
name="machines_extension_1",
|
||||||
|
need_infra=False,
|
||||||
|
# Do not set origin because of circular dependency
|
||||||
|
origin_v6="2001:db8:1234::",
|
||||||
|
soa=cls.machines_soa_1 # Dep machines.SOA
|
||||||
|
)
|
||||||
|
cls.machines_vlan_1 = machines.Vlan.objects.create(
|
||||||
|
vlan_id=0,
|
||||||
|
name="machines_vlan_1",
|
||||||
|
comment="machines Vlan 1"
|
||||||
|
)
|
||||||
|
cls.machines_iptype_1 = machines.IpType.objects.create(
|
||||||
|
type="machines_iptype_1",
|
||||||
|
extension=cls.machines_extension_1, # Dep machines.Extension
|
||||||
|
need_infra=False,
|
||||||
|
domaine_ip_start="10.0.0.1",
|
||||||
|
domaine_ip_stop="10.0.0.255",
|
||||||
|
prefix_v6="2001:db8:1234::",
|
||||||
|
vlan=cls.machines_vlan_1, # Dep machines.Vlan
|
||||||
|
ouverture_ports=cls.machines_ouvertureportlist_1 # Dep machines.OuverturePortList
|
||||||
|
)
|
||||||
|
# All IPs in the IpType range are autocreated so we can't create
|
||||||
|
# new ones and thus we only retrieve it if needed in the tests
|
||||||
|
cls.machines_iplist_1 = machines.IpList.objects.get(
|
||||||
|
ipv4="10.0.0.1",
|
||||||
|
ip_type=cls.machines_iptype_1, # Dep machines.IpType
|
||||||
|
)
|
||||||
|
cls.machines_machinetype_1 = machines.MachineType.objects.create(
|
||||||
|
type="machines_machinetype_1",
|
||||||
|
ip_type=cls.machines_iptype_1, # Dep machines.IpType
|
||||||
|
)
|
||||||
|
cls.machines_interface_1 = machines.Interface.objects.create(
|
||||||
|
ipv4=cls.machines_iplist_1, # Dep machines.IpList
|
||||||
|
mac_address="00:00:00:00:00:00",
|
||||||
|
machine=cls.machines_machine_1, # Dep machines.Machine
|
||||||
|
type=cls.machines_machinetype_1, # Dep machines.MachineType
|
||||||
|
details="machines Interface 1",
|
||||||
|
#port_lists=[cls.machines_ouvertureportlist_1] # Dep machines.OuverturePortList
|
||||||
|
)
|
||||||
|
cls.machines_domain_1 = machines.Domain.objects.create(
|
||||||
|
interface_parent=cls.machines_interface_1, # Dep machines.Interface
|
||||||
|
name="machinesdomain",
|
||||||
|
extension=cls.machines_extension_1 # Dep machines.Extension
|
||||||
|
# Do no define cname for circular dependency
|
||||||
|
)
|
||||||
|
cls.machines_mx_1 = machines.Mx.objects.create(
|
||||||
|
zone=cls.machines_extension_1, # Dep machines.Extension
|
||||||
|
priority=10,
|
||||||
|
name=cls.machines_domain_1 # Dep machines.Domain
|
||||||
|
)
|
||||||
|
cls.machines_ns_1 = machines.Ns.objects.create(
|
||||||
|
zone=cls.machines_extension_1, # Dep machines.Extension
|
||||||
|
ns=cls.machines_domain_1 # Dep machines.Domain
|
||||||
|
)
|
||||||
|
cls.machines_txt_1 = machines.Txt.objects.create(
|
||||||
|
zone=cls.machines_extension_1, # Dep machines.Extension
|
||||||
|
field1="machines_txt_1",
|
||||||
|
field2="machies Txt 1"
|
||||||
|
)
|
||||||
|
cls.machines_srv_1 = machines.Srv.objects.create(
|
||||||
|
service="machines_srv_1",
|
||||||
|
protocole=machines.Srv.TCP,
|
||||||
|
extension=cls.machines_extension_1, # Dep machines.Extension
|
||||||
|
ttl=172800,
|
||||||
|
priority=0,
|
||||||
|
port=1,
|
||||||
|
target=cls.machines_domain_1, # Dep machines.Domain
|
||||||
|
)
|
||||||
|
cls.machines_ipv6list_1 = machines.Ipv6List.objects.create(
|
||||||
|
ipv6="2001:db8:1234::",
|
||||||
|
interface=cls.machines_interface_1, # Dep machines.Interface
|
||||||
|
slaac_ip=False
|
||||||
|
)
|
||||||
|
cls.machines_service_1 = machines.Service.objects.create(
|
||||||
|
service_type="machines_service_1",
|
||||||
|
min_time_regen=datetime.timedelta(minutes=1),
|
||||||
|
regular_time_regen=datetime.timedelta(hours=1)
|
||||||
|
# Do not define service_link because circular dependency
|
||||||
|
)
|
||||||
|
cls.machines_servicelink_1 = machines.Service_link.objects.create(
|
||||||
|
service=cls.machines_service_1, # Dep machines.Service
|
||||||
|
server=cls.machines_interface_1, # Dep machines.Interface
|
||||||
|
last_regen=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
asked_regen=False
|
||||||
|
)
|
||||||
|
cls.machines_ouvertureport_1 = machines.OuverturePort.objects.create(
|
||||||
|
begin=1,
|
||||||
|
end=2,
|
||||||
|
port_list=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList
|
||||||
|
protocole=machines.OuverturePort.TCP,
|
||||||
|
io=machines.OuverturePort.OUT
|
||||||
|
)
|
||||||
|
cls.machines_nas_1 = machines.Nas.objects.create(
|
||||||
|
name="machines_nas_1",
|
||||||
|
nas_type=cls.machines_machinetype_1, # Dep machines.MachineType
|
||||||
|
machine_type=cls.machines_machinetype_1, # Dep machines.MachineType
|
||||||
|
port_access_mode=machines.Nas.AUTH[0][0],
|
||||||
|
autocapture_mac=False
|
||||||
|
)
|
||||||
|
cls.preferences_service_1 = preferences.Service.objects.create(
|
||||||
|
name="preferences_service_1",
|
||||||
|
url="https://example.net",
|
||||||
|
description="preferences Service 1",
|
||||||
|
image="/media/logo/none.png"
|
||||||
|
)
|
||||||
|
cls.topologie_stack_1 = topologie.Stack.objects.create(
|
||||||
|
name="topologie_stack_1",
|
||||||
|
stack_id="1",
|
||||||
|
details="topologie Stack 1",
|
||||||
|
member_id_min=1,
|
||||||
|
member_id_max=10
|
||||||
|
)
|
||||||
|
cls.topologie_accespoint_1 = topologie.AccessPoint.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
name="machines_machine_1",
|
||||||
|
active=True,
|
||||||
|
location="topologie AccessPoint 1"
|
||||||
|
)
|
||||||
|
cls.topologie_server_1 = topologie.Server.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
name="machines_machine_1",
|
||||||
|
active=True
|
||||||
|
)
|
||||||
|
cls.topologie_building_1 = topologie.Building.objects.create(
|
||||||
|
name="topologie_building_1"
|
||||||
|
)
|
||||||
|
cls.topologie_switchbay_1 = topologie.SwitchBay.objects.create(
|
||||||
|
name="topologie_switchbay_1",
|
||||||
|
building=cls.topologie_building_1, # Dep topologie.Building
|
||||||
|
info="topologie SwitchBay 1"
|
||||||
|
)
|
||||||
|
cls.topologie_constructorswitch_1 = topologie.ConstructorSwitch.objects.create(
|
||||||
|
name="topologie_constructorswitch_1"
|
||||||
|
)
|
||||||
|
cls.topologie_modelswitch_1 = topologie.ModelSwitch.objects.create(
|
||||||
|
reference="topologie_modelswitch_1",
|
||||||
|
constructor=cls.topologie_constructorswitch_1 # Dep topologie.ConstructorSwitch
|
||||||
|
)
|
||||||
|
cls.topologie_switch_1 = topologie.Switch.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
name="machines_machine_1",
|
||||||
|
active=True,
|
||||||
|
number=10,
|
||||||
|
stack=cls.topologie_stack_1, # Dep topologie.Stack
|
||||||
|
stack_member_id=1,
|
||||||
|
model=cls.topologie_modelswitch_1, # Dep topologie.ModelSwitch
|
||||||
|
switchbay=cls.topologie_switchbay_1 # Dep topologie.SwitchBay
|
||||||
|
)
|
||||||
|
cls.topologie_room_1 = topologie.Room.objects.create(
|
||||||
|
name="topologie_romm_1",
|
||||||
|
details="topologie Room 1"
|
||||||
|
)
|
||||||
|
cls.topologie_port_1 = topologie.Port.objects.create(
|
||||||
|
switch=cls.topologie_switch_1, # Dep topologie.Switch
|
||||||
|
port=1,
|
||||||
|
room=cls.topologie_room_1, # Dep topologie.Room
|
||||||
|
radius=topologie.Port.STATES[0][0],
|
||||||
|
vlan_force=cls.machines_vlan_1, # Dep machines.Vlan
|
||||||
|
details="topologie_switch_1"
|
||||||
|
)
|
||||||
|
cls.topologie_port_2 = topologie.Port.objects.create(
|
||||||
|
switch=cls.topologie_switch_1, # Dep topologie.Switch
|
||||||
|
port=2,
|
||||||
|
machine_interface=cls.machines_interface_1, # Dep machines.Interface
|
||||||
|
radius=topologie.Port.STATES[0][0],
|
||||||
|
vlan_force=cls.machines_vlan_1, # Dep machines.Vlan
|
||||||
|
details="topologie_switch_1"
|
||||||
|
)
|
||||||
|
cls.topologie_port_3 = topologie.Port.objects.create(
|
||||||
|
switch=cls.topologie_switch_1, # Dep topologie.Switch
|
||||||
|
port=3,
|
||||||
|
room=cls.topologie_room_1, # Dep topologie.Room
|
||||||
|
radius=topologie.Port.STATES[0][0],
|
||||||
|
# Do not defines related because circular dependency # Dep machines.Vlan
|
||||||
|
details="topologie_switch_1"
|
||||||
|
)
|
||||||
|
cls.users_ban_1 = users.Ban.objects.create(
|
||||||
|
user=cls.users_user_1, # Dep users.User
|
||||||
|
raison="users Ban 1",
|
||||||
|
date_start=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1),
|
||||||
|
state=users.Ban.STATES[0][0]
|
||||||
|
)
|
||||||
|
cls.users_club_1 = users.Club.objects.create(
|
||||||
|
password="password",
|
||||||
|
last_login=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
surname="users_club_1",
|
||||||
|
pseudo="usersclub1",
|
||||||
|
email="users_club_1@example.net",
|
||||||
|
school=cls.users_school_1, # Dep users.School
|
||||||
|
shell=cls.users_listshell_1, # Dep users.ListShell
|
||||||
|
comment="users Club 1 comment",
|
||||||
|
pwd_ntlm="",
|
||||||
|
state=users.User.STATES[0][0],
|
||||||
|
registered=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
telephone="0123456789",
|
||||||
|
uid_number=21103,
|
||||||
|
rezo_rez_uid=21103
|
||||||
|
)
|
||||||
|
# Need merge of MR145 to work
|
||||||
|
# TODO: Merge !145
|
||||||
|
# cls.users_listright_1 = users.ListRight.objects.create(
|
||||||
|
# unix_name="userslistright",
|
||||||
|
# gid=601,
|
||||||
|
# critical=False,
|
||||||
|
# details="userslistright"
|
||||||
|
# )
|
||||||
|
cls.users_serviceuser_1 = users.ServiceUser.objects.create(
|
||||||
|
password="password",
|
||||||
|
last_login=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
pseudo="usersserviceuser1",
|
||||||
|
access_group=users.ServiceUser.ACCESS[0][0],
|
||||||
|
comment="users ServiceUser 1"
|
||||||
|
)
|
||||||
|
cls.users_whitelist_1 = users.Whitelist.objects.create(
|
||||||
|
user=cls.users_user_1,
|
||||||
|
raison="users Whitelist 1",
|
||||||
|
date_start=datetime.datetime.now(datetime.timezone.utc),
|
||||||
|
date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_responses_code(self, urls, expected_code, formats=None,
|
||||||
|
assert_more=None):
|
||||||
|
"""Utility function to test if a list of urls answer an expected code.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
urls: The list of urls to test
|
||||||
|
expected_code: The HTTP return code expected
|
||||||
|
formats: The list of formats to use for the request. Default is to
|
||||||
|
only test `None` format.
|
||||||
|
assert_more: An optional function to assert more specific data in
|
||||||
|
the same test. The response object, the url and the format
|
||||||
|
used are passed as arguments.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: The response got did not have the expected status
|
||||||
|
code.
|
||||||
|
Any exception raised in the evalutation of `assert_more`.
|
||||||
|
"""
|
||||||
|
if formats is None:
|
||||||
|
formats = [None]
|
||||||
|
for url in urls:
|
||||||
|
for format in formats:
|
||||||
|
with self.subTest(url=url, format=format):
|
||||||
|
response = self.client.get(url, format=format)
|
||||||
|
assert response.status_code == expected_code
|
||||||
|
if assert_more is not None:
|
||||||
|
assert_more(response, url, format)
|
||||||
|
|
||||||
|
def test_no_auth_endpoints_with_no_auth(self):
|
||||||
|
"""Tests that every endpoint that does not require to be
|
||||||
|
authenticated, returns a Ok (200) response when not authenticated.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 200 status code.
|
||||||
|
"""
|
||||||
|
urls = self.no_auth_endpoints
|
||||||
|
self.check_responses_code(urls, codes.ok)
|
||||||
|
|
||||||
|
def test_auth_endpoints_with_no_auth(self):
|
||||||
|
"""Tests that every endpoint that does require to be authenticated,
|
||||||
|
returns a Unauthorized (401) response when not authenticated.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 401 status code.
|
||||||
|
"""
|
||||||
|
urls = self.auth_no_perm_endpoints + self.auth_perm_endpoints
|
||||||
|
self.check_responses_code(urls, codes.unauthorized)
|
||||||
|
|
||||||
|
def test_no_auth_endpoints_with_auth(self):
|
||||||
|
"""Tests that every endpoint that does not require to be
|
||||||
|
authenticated, returns a Ok (200) response when authenticated.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 200 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.stduser)
|
||||||
|
urls = self.no_auth_endpoints
|
||||||
|
self.check_responses_code(urls, codes.ok)
|
||||||
|
|
||||||
|
def test_auth_no_perm_endpoints_with_auth_and_no_perm(self):
|
||||||
|
"""Tests that every endpoint that does require to be authenticated and
|
||||||
|
no special permissions, returns a Ok (200) response when authenticated
|
||||||
|
but without permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 200 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.stduser)
|
||||||
|
urls = self.auth_no_perm_endpoints
|
||||||
|
self.check_responses_code(urls, codes.ok)
|
||||||
|
|
||||||
|
def test_auth_perm_endpoints_with_auth_and_no_perm(self):
|
||||||
|
"""Tests that every endpoint that does require to be authenticated and
|
||||||
|
special permissions, returns a Forbidden (403) response when
|
||||||
|
authenticated but without permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 403 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.stduser)
|
||||||
|
urls = self.auth_perm_endpoints
|
||||||
|
self.check_responses_code(urls, codes.forbidden)
|
||||||
|
|
||||||
|
def test_auth_endpoints_with_auth_and_perm(self):
|
||||||
|
"""Tests that every endpoint that does require to be authenticated,
|
||||||
|
returns a Ok (200) response when authenticated with all permissions.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 200 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.superuser)
|
||||||
|
urls = self.auth_no_perm_endpoints + self.auth_perm_endpoints
|
||||||
|
self.check_responses_code(urls, codes.ok)
|
||||||
|
|
||||||
|
def test_endpoints_not_found(self):
|
||||||
|
"""Tests that every endpoint that uses a primary key parameter,
|
||||||
|
returns a Not Found (404) response when queried with non-existing
|
||||||
|
primary key.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 404 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.superuser)
|
||||||
|
# Select only the URLs with '<pk>' and replace it with '42'
|
||||||
|
urls = self.not_found_endpoints
|
||||||
|
self.check_responses_code(urls, codes.not_found)
|
||||||
|
|
||||||
|
def test_formats(self):
|
||||||
|
"""Tests that every endpoint returns a Ok (200) response when using
|
||||||
|
different formats. Also checks that 'json' format returns a valid
|
||||||
|
JSON object.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have a 200 status code.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.superuser)
|
||||||
|
|
||||||
|
urls = self.no_auth_endpoints + self.auth_no_perm_endpoints + \
|
||||||
|
self.auth_perm_endpoints
|
||||||
|
|
||||||
|
def assert_more(response, url, format):
|
||||||
|
"""Assert the response is valid json when format is json"""
|
||||||
|
if format is 'json':
|
||||||
|
json.loads(response.content.decode())
|
||||||
|
|
||||||
|
self.check_responses_code(urls, codes.ok,
|
||||||
|
formats=[None, 'json', 'api'],
|
||||||
|
assert_more=assert_more)
|
||||||
|
|
||||||
|
class APIPaginationTestCase(APITestCase):
|
||||||
|
"""Test case to check that the pagination is used on all endpoints that
|
||||||
|
should use it.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
endpoints: A list of endpoints that should use the pagination.
|
||||||
|
superuser: A superuser used in the tests to access the endpoints.
|
||||||
|
"""
|
||||||
|
|
||||||
|
endpoints = [
|
||||||
|
'/api/cotisations/article/',
|
||||||
|
'/api/cotisations/banque/',
|
||||||
|
'/api/cotisations/cotisation/',
|
||||||
|
'/api/cotisations/facture/',
|
||||||
|
'/api/cotisations/paiement/',
|
||||||
|
'/api/cotisations/vente/',
|
||||||
|
'/api/machines/domain/',
|
||||||
|
'/api/machines/extension/',
|
||||||
|
'/api/machines/interface/',
|
||||||
|
'/api/machines/iplist/',
|
||||||
|
'/api/machines/iptype/',
|
||||||
|
'/api/machines/ipv6list/',
|
||||||
|
'/api/machines/machine/',
|
||||||
|
'/api/machines/machinetype/',
|
||||||
|
'/api/machines/mx/',
|
||||||
|
'/api/machines/nas/',
|
||||||
|
'/api/machines/ns/',
|
||||||
|
'/api/machines/ouvertureportlist/',
|
||||||
|
'/api/machines/ouvertureport/',
|
||||||
|
'/api/machines/servicelink/',
|
||||||
|
'/api/machines/service/',
|
||||||
|
'/api/machines/soa/',
|
||||||
|
'/api/machines/srv/',
|
||||||
|
'/api/machines/txt/',
|
||||||
|
'/api/machines/vlan/',
|
||||||
|
'/api/preferences/service/',
|
||||||
|
'/api/topologie/acesspoint/',
|
||||||
|
'/api/topologie/building/',
|
||||||
|
'/api/topologie/constructorswitch/',
|
||||||
|
'/api/topologie/modelswitch/',
|
||||||
|
'/api/topologie/room/',
|
||||||
|
'/api/topologie/server/',
|
||||||
|
'/api/topologie/stack/',
|
||||||
|
'/api/topologie/switch/',
|
||||||
|
'/api/topologie/switchbay/',
|
||||||
|
'/api/topologie/switchport/',
|
||||||
|
'/api/users/adherent/',
|
||||||
|
'/api/users/ban/',
|
||||||
|
'/api/users/club/',
|
||||||
|
'/api/users/listright/',
|
||||||
|
'/api/users/school/',
|
||||||
|
'/api/users/serviceuser/',
|
||||||
|
'/api/users/shell/',
|
||||||
|
'/api/users/user/',
|
||||||
|
'/api/users/whitelist/',
|
||||||
|
'/api/dns/zones/',
|
||||||
|
'/api/dhcp/hostmacip/',
|
||||||
|
'/api/mailing/standard',
|
||||||
|
'/api/mailing/club',
|
||||||
|
'/api/services/regen/',
|
||||||
|
]
|
||||||
|
superuser = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
# A user with all the rights
|
||||||
|
# We need to use a different username than for the first
|
||||||
|
# test case because TestCase is using rollbacks which don't
|
||||||
|
# trigger the ldap_sync() thus the LDAP still have data about
|
||||||
|
# the old users.
|
||||||
|
cls.superuser = users.User.objects.create_superuser(
|
||||||
|
"apisuperuser2",
|
||||||
|
"apisuperuser2",
|
||||||
|
"apisuperuser2@example.net",
|
||||||
|
"apisuperuser2"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.superuser.delete()
|
||||||
|
super().tearDownClass()
|
||||||
|
|
||||||
|
def test_pagination(self):
|
||||||
|
"""Tests that every endpoint is using the pagination correctly.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: An endpoint did not have one the following keyword
|
||||||
|
in the JSOn response: 'count', 'next', 'previous', 'results'
|
||||||
|
or more that 100 results were returned.
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.superuser)
|
||||||
|
for url in self.endpoints:
|
||||||
|
with self.subTest(url=url):
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
res_json = json.loads(response.content.decode())
|
||||||
|
assert 'count' in res_json.keys()
|
||||||
|
assert 'next' in res_json.keys()
|
||||||
|
assert 'previous' in res_json.keys()
|
||||||
|
assert 'results' in res_json.keys()
|
||||||
|
assert not len('results') > 100
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
|
125
api/urls.py
125
api/urls.py
|
@ -2,7 +2,7 @@
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
#
|
#
|
||||||
# Copyright © 2018 Mael Kervella
|
# Copyright © 2018 Maël Kervella
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,55 +17,92 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
"""api.urls
|
|
||||||
|
|
||||||
Urls de l'api, pointant vers les fonctions de views
|
"""Defines the URLs of the API
|
||||||
|
|
||||||
|
A custom router is used to register all the routes. That allows to register
|
||||||
|
all the URL patterns from the viewsets as a standard router but, the views
|
||||||
|
can also be register. That way a complete API root page presenting all URLs
|
||||||
|
can be generated automatically.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
from django.conf.urls import url
|
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
from .routers import AllViewsRouter
|
||||||
|
|
||||||
|
|
||||||
|
router = AllViewsRouter()
|
||||||
|
# COTISATIONS
|
||||||
|
router.register_viewset(r'cotisations/facture', views.FactureViewSet)
|
||||||
|
router.register_viewset(r'cotisations/vente', views.VenteViewSet)
|
||||||
|
router.register_viewset(r'cotisations/article', views.ArticleViewSet)
|
||||||
|
router.register_viewset(r'cotisations/banque', views.BanqueViewSet)
|
||||||
|
router.register_viewset(r'cotisations/paiement', views.PaiementViewSet)
|
||||||
|
router.register_viewset(r'cotisations/cotisation', views.CotisationViewSet)
|
||||||
|
# MACHINES
|
||||||
|
router.register_viewset(r'machines/machine', views.MachineViewSet)
|
||||||
|
router.register_viewset(r'machines/machinetype', views.MachineTypeViewSet)
|
||||||
|
router.register_viewset(r'machines/iptype', views.IpTypeViewSet)
|
||||||
|
router.register_viewset(r'machines/vlan', views.VlanViewSet)
|
||||||
|
router.register_viewset(r'machines/nas', views.NasViewSet)
|
||||||
|
router.register_viewset(r'machines/soa', views.SOAViewSet)
|
||||||
|
router.register_viewset(r'machines/extension', views.ExtensionViewSet)
|
||||||
|
router.register_viewset(r'machines/mx', views.MxViewSet)
|
||||||
|
router.register_viewset(r'machines/ns', views.NsViewSet)
|
||||||
|
router.register_viewset(r'machines/txt', views.TxtViewSet)
|
||||||
|
router.register_viewset(r'machines/srv', views.SrvViewSet)
|
||||||
|
router.register_viewset(r'machines/interface', views.InterfaceViewSet)
|
||||||
|
router.register_viewset(r'machines/ipv6list', views.Ipv6ListViewSet)
|
||||||
|
router.register_viewset(r'machines/domain', views.DomainViewSet)
|
||||||
|
router.register_viewset(r'machines/iplist', views.IpListViewSet)
|
||||||
|
router.register_viewset(r'machines/service', views.ServiceViewSet)
|
||||||
|
router.register_viewset(r'machines/servicelink', views.ServiceLinkViewSet, base_name='servicelink')
|
||||||
|
router.register_viewset(r'machines/ouvertureportlist', views.OuverturePortListViewSet)
|
||||||
|
router.register_viewset(r'machines/ouvertureport', views.OuverturePortViewSet)
|
||||||
|
# PREFERENCES
|
||||||
|
router.register_view(r'preferences/optionaluser', views.OptionalUserView),
|
||||||
|
router.register_view(r'preferences/optionalmachine', views.OptionalMachineView),
|
||||||
|
router.register_view(r'preferences/optionaltopologie', views.OptionalTopologieView),
|
||||||
|
router.register_view(r'preferences/generaloption', views.GeneralOptionView),
|
||||||
|
router.register_viewset(r'preferences/service', views.HomeServiceViewSet, base_name='homeservice'),
|
||||||
|
router.register_view(r'preferences/assooption', views.AssoOptionView),
|
||||||
|
router.register_view(r'preferences/homeoption', views.HomeOptionView),
|
||||||
|
router.register_view(r'preferences/mailmessageoption', views.MailMessageOptionView),
|
||||||
|
# TOPOLOGIE
|
||||||
|
router.register_viewset(r'topologie/stack', views.StackViewSet)
|
||||||
|
router.register_viewset(r'topologie/acesspoint', views.AccessPointViewSet)
|
||||||
|
router.register_viewset(r'topologie/switch', views.SwitchViewSet)
|
||||||
|
router.register_viewset(r'topologie/server', views.ServerViewSet)
|
||||||
|
router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet)
|
||||||
|
router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet)
|
||||||
|
router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet)
|
||||||
|
router.register_viewset(r'topologie/building', views.BuildingViewSet)
|
||||||
|
router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport')
|
||||||
|
router.register_viewset(r'topologie/room', views.RoomViewSet)
|
||||||
|
# USERS
|
||||||
|
router.register_viewset(r'users/user', views.UserViewSet)
|
||||||
|
router.register_viewset(r'users/club', views.ClubViewSet)
|
||||||
|
router.register_viewset(r'users/adherent', views.AdherentViewSet)
|
||||||
|
router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet)
|
||||||
|
router.register_viewset(r'users/school', views.SchoolViewSet)
|
||||||
|
router.register_viewset(r'users/listright', views.ListRightViewSet)
|
||||||
|
router.register_viewset(r'users/shell', views.ShellViewSet, base_name='shell')
|
||||||
|
router.register_viewset(r'users/ban', views.BanViewSet)
|
||||||
|
router.register_viewset(r'users/whitelist', views.WhitelistViewSet)
|
||||||
|
# SERVICE REGEN
|
||||||
|
router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name='serviceregen')
|
||||||
|
# DHCP
|
||||||
|
router.register_view(r'dhcp/hostmacip', views.HostMacIpView),
|
||||||
|
# DNS
|
||||||
|
router.register_view(r'dns/zones', views.DNSZonesView),
|
||||||
|
# MAILING
|
||||||
|
router.register_view(r'mailing/standard', views.StandardMailingView),
|
||||||
|
router.register_view(r'mailing/club', views.ClubMailingView),
|
||||||
|
# TOKEN AUTHENTICATION
|
||||||
|
router.register_view(r'token-auth', views.ObtainExpiringAuthToken)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Services
|
url(r'^', include(router.urls)),
|
||||||
url(r'^services/$', views.services),
|
|
||||||
url(
|
|
||||||
r'^services/(?P<server_name>\w+)/(?P<service_name>\w+)/regen/$',
|
|
||||||
views.services_server_service_regen
|
|
||||||
),
|
|
||||||
url(r'^services/(?P<server_name>\w+)/$', views.services_server),
|
|
||||||
|
|
||||||
# DNS
|
|
||||||
url(r'^dns/mac-ip-dns/$', views.dns_mac_ip_dns),
|
|
||||||
url(r'^dns/alias/$', views.dns_alias),
|
|
||||||
url(r'^dns/corresp/$', views.dns_corresp),
|
|
||||||
url(r'^dns/mx/$', views.dns_mx),
|
|
||||||
url(r'^dns/ns/$', views.dns_ns),
|
|
||||||
url(r'^dns/txt/$', views.dns_txt),
|
|
||||||
url(r'^dns/srv/$', views.dns_srv),
|
|
||||||
url(r'^dns/zones/$', views.dns_zones),
|
|
||||||
|
|
||||||
# Unifi controler AP names
|
|
||||||
url(r'^unifi/ap_names/$', views.accesspoint_ip_dns),
|
|
||||||
|
|
||||||
# Firewall
|
|
||||||
url(r'^firewall/ouverture_ports/$', views.firewall_ouverture_ports),
|
|
||||||
|
|
||||||
# DHCP
|
|
||||||
url(r'^dhcp/mac-ip/$', views.dhcp_mac_ip),
|
|
||||||
|
|
||||||
# Mailings
|
|
||||||
url(r'^mailing/standard/$', views.mailing_standard),
|
|
||||||
url(
|
|
||||||
r'^mailing/standard/(?P<ml_name>\w+)/members/$',
|
|
||||||
views.mailing_standard_ml_members
|
|
||||||
),
|
|
||||||
url(r'^mailing/club/$', views.mailing_club),
|
|
||||||
url(
|
|
||||||
r'^mailing/club/(?P<ml_name>\w+)/members/$',
|
|
||||||
views.mailing_club_ml_members
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
123
api/utils.py
123
api/utils.py
|
@ -1,123 +0,0 @@
|
||||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
|
||||||
# quelques clics.
|
|
||||||
#
|
|
||||||
# Copyright © 2018 Maël Kervella
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
"""api.utils.
|
|
||||||
|
|
||||||
Set of various and usefull functions for the API app
|
|
||||||
"""
|
|
||||||
|
|
||||||
from rest_framework.renderers import JSONRenderer
|
|
||||||
from django.http import HttpResponse
|
|
||||||
|
|
||||||
|
|
||||||
class JSONResponse(HttpResponse):
|
|
||||||
"""A JSON response that can be send as an HTTP response.
|
|
||||||
Usefull in case of REST API.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data, **kwargs):
|
|
||||||
"""Initialisz a JSONResponse object.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: the data to render as JSON (often made of lists, dicts,
|
|
||||||
strings, boolean and numbers). See `JSONRenderer.render(data)` for
|
|
||||||
further details.
|
|
||||||
|
|
||||||
Creates:
|
|
||||||
An HTTPResponse containing the data in JSON format.
|
|
||||||
"""
|
|
||||||
|
|
||||||
content = JSONRenderer().render(data)
|
|
||||||
kwargs['content_type'] = 'application/json'
|
|
||||||
super(JSONResponse, self).__init__(content, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class JSONError(JSONResponse):
|
|
||||||
"""A JSON response when the request failed.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg, data=None, **kwargs):
|
|
||||||
"""Initialise a JSONError object.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
error_msg: A message explaining where the error is.
|
|
||||||
data: An optional field for further data to send along.
|
|
||||||
|
|
||||||
Creates:
|
|
||||||
A JSONResponse containing a field `status` set to `error` and a
|
|
||||||
field `reason` containing `error_msg`. If `data` argument has been
|
|
||||||
given, a field `data` containing it is added to the JSON response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
response = {
|
|
||||||
'status': 'error',
|
|
||||||
'reason': error_msg
|
|
||||||
}
|
|
||||||
if data is not None:
|
|
||||||
response['data'] = data
|
|
||||||
super(JSONError, self).__init__(response, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class JSONSuccess(JSONResponse):
|
|
||||||
"""A JSON response when the request suceeded.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data=None, **kwargs):
|
|
||||||
"""Initialise a JSONSucess object.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
error_msg: A message explaining where the error is.
|
|
||||||
data: An optional field for further data to send along.
|
|
||||||
|
|
||||||
Creates:
|
|
||||||
A JSONResponse containing a field `status` set to `sucess`. If
|
|
||||||
`data` argument has been given, a field `data` containing it is
|
|
||||||
added to the JSON response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
response = {
|
|
||||||
'status': 'success',
|
|
||||||
}
|
|
||||||
if data is not None:
|
|
||||||
response['data'] = data
|
|
||||||
super(JSONSuccess, self).__init__(response, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def accept_method(methods):
|
|
||||||
"""Decorator to set a list of accepted request method.
|
|
||||||
Check if the method used is accepted. If not, send a NotAllowed response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def decorator(view):
|
|
||||||
"""The decorator to use on a specific view
|
|
||||||
"""
|
|
||||||
def wrapper(request, *args, **kwargs):
|
|
||||||
"""The wrapper used for a specific request
|
|
||||||
"""
|
|
||||||
if request.method in methods:
|
|
||||||
return view(request, *args, **kwargs)
|
|
||||||
else:
|
|
||||||
return JSONError(
|
|
||||||
'Invalid request method. Request methods authorize are ' +
|
|
||||||
str(methods)
|
|
||||||
)
|
|
||||||
return view(request, *args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
1108
api/views.py
1108
api/views.py
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,7 @@
|
||||||
WSGIScriptAlias / PATH/re2o/wsgi.py
|
WSGIScriptAlias / PATH/re2o/wsgi.py
|
||||||
WSGIProcessGroup re2o
|
WSGIProcessGroup re2o
|
||||||
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
||||||
|
WSGIPassAuthorization On
|
||||||
|
|
||||||
SSLCertificateFile /etc/letsencrypt/live/LE_PATH/fullchain.pem
|
SSLCertificateFile /etc/letsencrypt/live/LE_PATH/fullchain.pem
|
||||||
SSLCertificateKeyFile /etc/letsencrypt/live/LE_PATH/privkey.pem
|
SSLCertificateKeyFile /etc/letsencrypt/live/LE_PATH/privkey.pem
|
||||||
|
|
|
@ -19,5 +19,6 @@
|
||||||
WSGIScriptAlias / PATH/re2o/wsgi.py
|
WSGIScriptAlias / PATH/re2o/wsgi.py
|
||||||
WSGIProcessGroup re2o
|
WSGIProcessGroup re2o
|
||||||
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
||||||
|
WSGIPassAuthorization On
|
||||||
|
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
19
machines/migrations/0082_auto_20180525_2209.py
Normal file
19
machines/migrations/0082_auto_20180525_2209.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-05-25 20:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0081_auto_20180521_1413'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='service_link',
|
||||||
|
options={'permissions': (('view_service_link', 'Peut voir un objet service_link'),)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -562,6 +562,25 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
entry += "@ IN AAAA " + str(self.origin_v6)
|
entry += "@ IN AAAA " + str(self.origin_v6)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
def get_associated_a_records(self):
|
||||||
|
from re2o.utils import all_active_assigned_interfaces
|
||||||
|
return (all_active_assigned_interfaces()
|
||||||
|
.filter(type__ip_type__extension=self)
|
||||||
|
.filter(ipv4__isnull=False))
|
||||||
|
|
||||||
|
def get_associated_aaaa_records(self):
|
||||||
|
from re2o.utils import all_active_interfaces
|
||||||
|
return (all_active_interfaces(full=True)
|
||||||
|
.filter(type__ip_type__extension=self))
|
||||||
|
|
||||||
|
def get_associated_cname_records(self):
|
||||||
|
from re2o.utils import all_active_assigned_interfaces
|
||||||
|
return (Domain.objects
|
||||||
|
.filter(extension=self)
|
||||||
|
.filter(cname__isnull=False)
|
||||||
|
.filter(interface_parent__in=all_active_assigned_interfaces())
|
||||||
|
.prefetch_related('cname'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_use_all(user_request, *_args, **_kwargs):
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
||||||
|
@ -1388,12 +1407,18 @@ class Service_link(RevMixin, AclMixin, models.Model):
|
||||||
last_regen = models.DateTimeField(auto_now_add=True)
|
last_regen = models.DateTimeField(auto_now_add=True)
|
||||||
asked_regen = models.BooleanField(default=False)
|
asked_regen = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
permissions = (
|
||||||
|
("view_service_link", "Peut voir un objet service_link"),
|
||||||
|
)
|
||||||
|
|
||||||
def done_regen(self):
|
def done_regen(self):
|
||||||
""" Appellé lorsqu'un serveur a regénéré son service"""
|
""" Appellé lorsqu'un serveur a regénéré son service"""
|
||||||
self.last_regen = timezone.now()
|
self.last_regen = timezone.now()
|
||||||
self.asked_regen = False
|
self.asked_regen = False
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
@property
|
||||||
def need_regen(self):
|
def need_regen(self):
|
||||||
""" Décide si le temps minimal écoulé est suffisant pour provoquer une
|
""" Décide si le temps minimal écoulé est suffisant pour provoquer une
|
||||||
régénération de service"""
|
régénération de service"""
|
||||||
|
@ -1406,6 +1431,19 @@ class Service_link(RevMixin, AclMixin, models.Model):
|
||||||
) < timezone.now()
|
) < timezone.now()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@need_regen.setter
|
||||||
|
def need_regen(self, value):
|
||||||
|
"""
|
||||||
|
Force to set the need_regen value. True means a regen is asked and False
|
||||||
|
means a regen has been done.
|
||||||
|
|
||||||
|
:param value: (bool) The value to set to
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
self.last_regen = timezone.now()
|
||||||
|
self.asked_regen = value
|
||||||
|
self.save()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.server) + " " + str(self.service)
|
return str(self.server) + " " + str(self.service)
|
||||||
|
|
||||||
|
|
|
@ -376,7 +376,7 @@ class ServiceServersSerializer(serializers.ModelSerializer):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_regen_status(obj):
|
def get_regen_status(obj):
|
||||||
""" The string representation of the regen status """
|
""" The string representation of the regen status """
|
||||||
return obj.need_regen()
|
return obj.need_regen
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortsSerializer(serializers.Serializer):
|
class OuverturePortsSerializer(serializers.Serializer):
|
||||||
|
|
2
pip_dev_requirements.txt
Normal file
2
pip_dev_requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-r pip_requirements.txt
|
||||||
|
volatildap
|
|
@ -75,7 +75,6 @@ LOCAL_APPS = (
|
||||||
're2o',
|
're2o',
|
||||||
'preferences',
|
'preferences',
|
||||||
'logs',
|
'logs',
|
||||||
'api',
|
|
||||||
)
|
)
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
DJANGO_CONTRIB_APPS +
|
DJANGO_CONTRIB_APPS +
|
||||||
|
@ -174,3 +173,8 @@ GRAPH_MODELS = {
|
||||||
'all_applications': True,
|
'all_applications': True,
|
||||||
'group_models': True,
|
'group_models': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Activate API
|
||||||
|
if 'api' in INSTALLED_APPS:
|
||||||
|
from api.settings import *
|
||||||
|
INSTALLED_APPS += API_APPS
|
||||||
|
|
|
@ -56,6 +56,10 @@ DATABASES = {
|
||||||
'USER': 'db_user_value',
|
'USER': 'db_user_value',
|
||||||
'PASSWORD': DB_PASSWORD,
|
'PASSWORD': DB_PASSWORD,
|
||||||
'HOST': 'db_host_value',
|
'HOST': 'db_host_value',
|
||||||
|
'TEST': {
|
||||||
|
'CHARSET': 'utf8',
|
||||||
|
'COLLATION': 'utf8_general_ci'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'ldap': { # The LDAP
|
'ldap': { # The LDAP
|
||||||
'ENGINE': 'ldapdb.backends.ldap',
|
'ENGINE': 'ldapdb.backends.ldap',
|
||||||
|
|
|
@ -42,6 +42,7 @@ Including another URLconf
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
|
@ -70,6 +71,8 @@ urlpatterns = [
|
||||||
r'^preferences/',
|
r'^preferences/',
|
||||||
include('preferences.urls', namespace='preferences')
|
include('preferences.urls', namespace='preferences')
|
||||||
),
|
),
|
||||||
url(r'^api/', include('api.urls', namespace='api')),
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
if 'api' in settings.INSTALLED_APPS:
|
||||||
|
urlpatterns += [
|
||||||
|
url(r'^api/', include('api.urls', namespace='api')),
|
||||||
|
]
|
||||||
|
|
0
test_utils/__init__.py
Normal file
0
test_utils/__init__.py
Normal file
564
test_utils/ldap/schema/radius.schema
Normal file
564
test_utils/ldap/schema/radius.schema
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
# This is a LDAPv3 schema for RADIUS attributes.
|
||||||
|
# Tested on OpenLDAP 2.0.7
|
||||||
|
# Posted by Javier Fernandez-Sanguino Pena <jfernandez@sgi.es>
|
||||||
|
# LDAP v3 version by Jochen Friedrich <jochen@scram.de>
|
||||||
|
# Updates by Adrian Pavlykevych <pam@polynet.lviv.ua>
|
||||||
|
##############
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.1
|
||||||
|
NAME 'radiusArapFeatures'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.2
|
||||||
|
NAME 'radiusArapSecurity'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.3
|
||||||
|
NAME 'radiusArapZoneAccess'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.44
|
||||||
|
NAME 'radiusAuthType'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.4
|
||||||
|
NAME 'radiusCallbackId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.5
|
||||||
|
NAME 'radiusCallbackNumber'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.6
|
||||||
|
NAME 'radiusCalledStationId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.7
|
||||||
|
NAME 'radiusCallingStationId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.8
|
||||||
|
NAME 'radiusClass'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.45
|
||||||
|
NAME 'radiusClientIPAddress'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.9
|
||||||
|
NAME 'radiusFilterId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.10
|
||||||
|
NAME 'radiusFramedAppleTalkLink'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.11
|
||||||
|
NAME 'radiusFramedAppleTalkNetwork'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.12
|
||||||
|
NAME 'radiusFramedAppleTalkZone'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.13
|
||||||
|
NAME 'radiusFramedCompression'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.14
|
||||||
|
NAME 'radiusFramedIPAddress'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.15
|
||||||
|
NAME 'radiusFramedIPNetmask'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.16
|
||||||
|
NAME 'radiusFramedIPXNetwork'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.17
|
||||||
|
NAME 'radiusFramedMTU'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.18
|
||||||
|
NAME 'radiusFramedProtocol'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.19
|
||||||
|
NAME 'radiusFramedRoute'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.20
|
||||||
|
NAME 'radiusFramedRouting'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.46
|
||||||
|
NAME 'radiusGroupName'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.47
|
||||||
|
NAME 'radiusHint'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.48
|
||||||
|
NAME 'radiusHuntgroupName'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.21
|
||||||
|
NAME 'radiusIdleTimeout'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.22
|
||||||
|
NAME 'radiusLoginIPHost'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.23
|
||||||
|
NAME 'radiusLoginLATGroup'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.24
|
||||||
|
NAME 'radiusLoginLATNode'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.25
|
||||||
|
NAME 'radiusLoginLATPort'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.26
|
||||||
|
NAME 'radiusLoginLATService'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.27
|
||||||
|
NAME 'radiusLoginService'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.28
|
||||||
|
NAME 'radiusLoginTCPPort'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.29
|
||||||
|
NAME 'radiusPasswordRetry'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.30
|
||||||
|
NAME 'radiusPortLimit'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.49
|
||||||
|
NAME 'radiusProfileDn'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY distinguishedNameMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.31
|
||||||
|
NAME 'radiusPrompt'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.50
|
||||||
|
NAME 'radiusProxyToRealm'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.51
|
||||||
|
NAME 'radiusReplicateToRealm'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.52
|
||||||
|
NAME 'radiusRealm'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.32
|
||||||
|
NAME 'radiusServiceType'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.33
|
||||||
|
NAME 'radiusSessionTimeout'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.34
|
||||||
|
NAME 'radiusTerminationAction'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.35
|
||||||
|
NAME 'radiusTunnelAssignmentId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.36
|
||||||
|
NAME 'radiusTunnelMediumType'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.37
|
||||||
|
NAME 'radiusTunnelPassword'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.38
|
||||||
|
NAME 'radiusTunnelPreference'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.39
|
||||||
|
NAME 'radiusTunnelPrivateGroupId'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.40
|
||||||
|
NAME 'radiusTunnelServerEndpoint'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.41
|
||||||
|
NAME 'radiusTunnelType'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.42
|
||||||
|
NAME 'radiusVSA'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.43
|
||||||
|
NAME 'radiusTunnelClientEndpoint'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#need to change asn1.id
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.53
|
||||||
|
NAME 'radiusSimultaneousUse'
|
||||||
|
DESC ''
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.54
|
||||||
|
NAME 'radiusLoginTime'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.55
|
||||||
|
NAME 'radiusUserCategory'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.56
|
||||||
|
NAME 'radiusStripUserName'
|
||||||
|
DESC ''
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.57
|
||||||
|
NAME 'dialupAccess'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.58
|
||||||
|
NAME 'radiusExpiration'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
SINGLE-VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.59
|
||||||
|
NAME 'radiusCheckItem'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
attributetype
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.1.60
|
||||||
|
NAME 'radiusReplyItem'
|
||||||
|
DESC ''
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
objectclass
|
||||||
|
( 1.3.6.1.4.1.3317.4.3.2.1
|
||||||
|
NAME 'radiusprofile'
|
||||||
|
SUP top AUXILIARY
|
||||||
|
DESC ''
|
||||||
|
MUST cn
|
||||||
|
MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $
|
||||||
|
radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
|
||||||
|
radiusCalledStationId $ radiusCallingStationId $ radiusClass $
|
||||||
|
radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $
|
||||||
|
radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $
|
||||||
|
radiusFramedCompression $ radiusFramedIPAddress $
|
||||||
|
radiusFramedIPNetmask $ radiusFramedIPXNetwork $
|
||||||
|
radiusFramedMTU $ radiusFramedProtocol $
|
||||||
|
radiusCheckItem $ radiusReplyItem $
|
||||||
|
radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $
|
||||||
|
radiusGroupName $ radiusHint $ radiusHuntgroupName $
|
||||||
|
radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $
|
||||||
|
radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $
|
||||||
|
radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $
|
||||||
|
radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $
|
||||||
|
radiusRealm $ radiusReplicateToRealm $ radiusServiceType $
|
||||||
|
radiusSessionTimeout $ radiusStripUserName $
|
||||||
|
radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDn $
|
||||||
|
radiusSimultaneousUse $ radiusTunnelAssignmentId $
|
||||||
|
radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $
|
||||||
|
radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $
|
||||||
|
radiusTunnelType $ radiusUserCategory $ radiusVSA $
|
||||||
|
radiusExpiration $ dialupAccess )
|
||||||
|
)
|
644
test_utils/ldap/schema/samba.schema
Normal file
644
test_utils/ldap/schema/samba.schema
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
##
|
||||||
|
## schema file for OpenLDAP 2.x
|
||||||
|
## Schema for storing Samba user accounts and group maps in LDAP
|
||||||
|
## OIDs are owned by the Samba Team
|
||||||
|
##
|
||||||
|
## Prerequisite schemas - uid (cosine.schema)
|
||||||
|
## - displayName (inetorgperson.schema)
|
||||||
|
## - gidNumber (nis.schema)
|
||||||
|
##
|
||||||
|
## 1.3.6.1.4.1.7165.2.1.x - attributetypes
|
||||||
|
## 1.3.6.1.4.1.7165.2.2.x - objectclasses
|
||||||
|
##
|
||||||
|
## Printer support
|
||||||
|
## 1.3.6.1.4.1.7165.2.3.1.x - attributetypes
|
||||||
|
## 1.3.6.1.4.1.7165.2.3.2.x - objectclasses
|
||||||
|
##
|
||||||
|
## Samba4
|
||||||
|
## 1.3.6.1.4.1.7165.4.1.x - attributetypes
|
||||||
|
## 1.3.6.1.4.1.7165.4.2.x - objectclasses
|
||||||
|
## 1.3.6.1.4.1.7165.4.3.x - LDB/LDAP Controls
|
||||||
|
## 1.3.6.1.4.1.7165.4.4.x - LDB/LDAP Extended Operations
|
||||||
|
## 1.3.6.1.4.1.7165.4.255.x - mapped OIDs due to conflicts between AD and standards-track
|
||||||
|
##
|
||||||
|
## External projects
|
||||||
|
## 1.3.6.1.4.1.7165.655.x
|
||||||
|
## 1.3.6.1.4.1.7165.655.1.x - GSS-NTLMSSP
|
||||||
|
##
|
||||||
|
## ----- READ THIS WHEN ADDING A NEW ATTRIBUTE OR OBJECT CLASS ------
|
||||||
|
##
|
||||||
|
## Run the 'get_next_oid' bash script in this directory to find the
|
||||||
|
## next available OID for attribute type and object classes.
|
||||||
|
##
|
||||||
|
## $ ./get_next_oid
|
||||||
|
## attributetype ( 1.3.6.1.4.1.7165.2.1.XX NAME ....
|
||||||
|
## objectclass ( 1.3.6.1.4.1.7165.2.2.XX NAME ....
|
||||||
|
##
|
||||||
|
## Also ensure that new entries adhere to the declaration style
|
||||||
|
## used throughout this file
|
||||||
|
##
|
||||||
|
## <attributetype|objectclass> ( 1.3.6.1.4.1.7165.2.XX.XX NAME ....
|
||||||
|
## ^ ^ ^
|
||||||
|
##
|
||||||
|
## The spaces are required for the get_next_oid script (and for
|
||||||
|
## readability).
|
||||||
|
##
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
|
||||||
|
# objectIdentifier SambaRoot 1.3.6.1.4.1.7165
|
||||||
|
# objectIdentifier Samba3 SambaRoot:2
|
||||||
|
# objectIdentifier Samba3Attrib Samba3:1
|
||||||
|
# objectIdentifier Samba3ObjectClass Samba3:2
|
||||||
|
# objectIdentifier Samba4 SambaRoot:4
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
## HISTORICAL ##
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
##
|
||||||
|
## Password hashes
|
||||||
|
##
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.1 NAME 'lmPassword'
|
||||||
|
# DESC 'LanManager Passwd'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.2 NAME 'ntPassword'
|
||||||
|
# DESC 'NT Passwd'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Account flags in string format ([UWDX ])
|
||||||
|
##
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.4 NAME 'acctFlags'
|
||||||
|
# DESC 'Account Flags'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Password timestamps & policies
|
||||||
|
##
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.3 NAME 'pwdLastSet'
|
||||||
|
# DESC 'NT pwdLastSet'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.5 NAME 'logonTime'
|
||||||
|
# DESC 'NT logonTime'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.6 NAME 'logoffTime'
|
||||||
|
# DESC 'NT logoffTime'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.7 NAME 'kickoffTime'
|
||||||
|
# DESC 'NT kickoffTime'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.8 NAME 'pwdCanChange'
|
||||||
|
# DESC 'NT pwdCanChange'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.9 NAME 'pwdMustChange'
|
||||||
|
# DESC 'NT pwdMustChange'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## string settings
|
||||||
|
##
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.10 NAME 'homeDrive'
|
||||||
|
# DESC 'NT homeDrive'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.11 NAME 'scriptPath'
|
||||||
|
# DESC 'NT scriptPath'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.12 NAME 'profilePath'
|
||||||
|
# DESC 'NT profilePath'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.13 NAME 'userWorkstations'
|
||||||
|
# DESC 'userWorkstations'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.17 NAME 'smbHome'
|
||||||
|
# DESC 'smbHome'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.18 NAME 'domain'
|
||||||
|
# DESC 'Windows NT domain to which the user belongs'
|
||||||
|
# EQUALITY caseIgnoreIA5Match
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||||
|
|
||||||
|
##
|
||||||
|
## user and group RID
|
||||||
|
##
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.14 NAME 'rid'
|
||||||
|
# DESC 'NT rid'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#attributetype ( 1.3.6.1.4.1.7165.2.1.15 NAME 'primaryGroupID'
|
||||||
|
# DESC 'NT Group RID'
|
||||||
|
# EQUALITY integerMatch
|
||||||
|
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## The smbPasswordEntry objectclass has been depreciated in favor of the
|
||||||
|
## sambaAccount objectclass
|
||||||
|
##
|
||||||
|
#objectclass ( 1.3.6.1.4.1.7165.2.2.1 NAME 'smbPasswordEntry' SUP top AUXILIARY
|
||||||
|
# DESC 'Samba smbpasswd entry'
|
||||||
|
# MUST ( uid $ uidNumber )
|
||||||
|
# MAY ( lmPassword $ ntPassword $ pwdLastSet $ acctFlags ))
|
||||||
|
|
||||||
|
#objectclass ( 1.3.6.1.4.1.7165.2.2.2 NAME 'sambaAccount' SUP top STRUCTURAL
|
||||||
|
# DESC 'Samba Account'
|
||||||
|
# MUST ( uid $ rid )
|
||||||
|
# MAY ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
|
||||||
|
# logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
|
||||||
|
# displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
|
||||||
|
# description $ userWorkstations $ primaryGroupID $ domain ))
|
||||||
|
|
||||||
|
#objectclass ( 1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' SUP top AUXILIARY
|
||||||
|
# DESC 'Samba Auxiliary Account'
|
||||||
|
# MUST ( uid $ rid )
|
||||||
|
# MAY ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
|
||||||
|
# logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
|
||||||
|
# displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
|
||||||
|
# description $ userWorkstations $ primaryGroupID $ domain ))
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
## END OF HISTORICAL ##
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
## Attributes used by Samba 3.0 schema ##
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
##
|
||||||
|
## Password hashes
|
||||||
|
##
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword'
|
||||||
|
DESC 'LanManager Password'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword'
|
||||||
|
DESC 'MD4 hash of the unicode password'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Account flags in string format ([UWDX ])
|
||||||
|
##
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags'
|
||||||
|
DESC 'Account Flags'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Password timestamps & policies
|
||||||
|
##
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet'
|
||||||
|
DESC 'Timestamp of the last password update'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange'
|
||||||
|
DESC 'Timestamp of when the user is allowed to update the password'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange'
|
||||||
|
DESC 'Timestamp of when the password will expire'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime'
|
||||||
|
DESC 'Timestamp of last logon'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime'
|
||||||
|
DESC 'Timestamp of last logoff'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime'
|
||||||
|
DESC 'Timestamp of when the user will be logged off automatically'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
|
||||||
|
DESC 'Bad password attempt count'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
|
||||||
|
DESC 'Time of the last bad password attempt'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.55 NAME 'sambaLogonHours'
|
||||||
|
DESC 'Logon Hours'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{42} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## string settings
|
||||||
|
##
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive'
|
||||||
|
DESC 'Driver letter of home directory mapping'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript'
|
||||||
|
DESC 'Logon script path'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath'
|
||||||
|
DESC 'Roaming profile path'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations'
|
||||||
|
DESC 'List of user workstations the user is allowed to logon to'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath'
|
||||||
|
DESC 'Home directory UNC path'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName'
|
||||||
|
DESC 'Windows NT domain to which the user belongs'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial'
|
||||||
|
DESC 'Base64 encoded user parameter string'
|
||||||
|
EQUALITY caseExactMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.54 NAME 'sambaPasswordHistory'
|
||||||
|
DESC 'Concatenated MD5 hashes of the salted NT passwords used on this account'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
|
||||||
|
|
||||||
|
##
|
||||||
|
## SID, of any type
|
||||||
|
##
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID'
|
||||||
|
DESC 'Security ID'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SUBSTR caseExactIA5SubstringsMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Primary group SID, compatible with ntSid
|
||||||
|
##
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
|
||||||
|
DESC 'Primary Group Security ID'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList'
|
||||||
|
DESC 'Security ID List'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} )
|
||||||
|
|
||||||
|
##
|
||||||
|
## group mapping attributes
|
||||||
|
##
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType'
|
||||||
|
DESC 'NT Group Type'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
##
|
||||||
|
## Store info on the domain
|
||||||
|
##
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid'
|
||||||
|
DESC 'Next NT rid to give our for users'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid'
|
||||||
|
DESC 'Next NT rid to give out for groups'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid'
|
||||||
|
DESC 'Next NT rid to give out for anything'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase'
|
||||||
|
DESC 'Base at which the samba RID generation algorithm should operate'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName'
|
||||||
|
DESC 'Share Name'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName'
|
||||||
|
DESC 'Option Name'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SUBSTR caseIgnoreSubstringsMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption'
|
||||||
|
DESC 'A boolean option'
|
||||||
|
EQUALITY booleanMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption'
|
||||||
|
DESC 'An integer option'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption'
|
||||||
|
DESC 'A string option'
|
||||||
|
EQUALITY caseExactIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption'
|
||||||
|
DESC 'A string list option'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||||
|
|
||||||
|
|
||||||
|
##attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME 'sambaPrivName'
|
||||||
|
## SUP name )
|
||||||
|
|
||||||
|
##attributetype ( 1.3.6.1.4.1.7165.2.1.52 NAME 'sambaPrivilegeList'
|
||||||
|
## DESC 'Privileges List'
|
||||||
|
## EQUALITY caseIgnoreIA5Match
|
||||||
|
## SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.53 NAME 'sambaTrustFlags'
|
||||||
|
DESC 'Trust Password Flags'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||||
|
|
||||||
|
# "min password length"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.58 NAME 'sambaMinPwdLength'
|
||||||
|
DESC 'Minimal password length (default: 5)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "password history"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.59 NAME 'sambaPwdHistoryLength'
|
||||||
|
DESC 'Length of Password History Entries (default: 0 => off)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "user must logon to change password"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.60 NAME 'sambaLogonToChgPwd'
|
||||||
|
DESC 'Force Users to logon for password change (default: 0 => off, 2 => on)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "maximum password age"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.61 NAME 'sambaMaxPwdAge'
|
||||||
|
DESC 'Maximum password age, in seconds (default: -1 => never expire passwords)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "minimum password age"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.62 NAME 'sambaMinPwdAge'
|
||||||
|
DESC 'Minimum password age, in seconds (default: 0 => allow immediate password change)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "lockout duration"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.63 NAME 'sambaLockoutDuration'
|
||||||
|
DESC 'Lockout duration in minutes (default: 30, -1 => forever)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "reset count minutes"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.64 NAME 'sambaLockoutObservationWindow'
|
||||||
|
DESC 'Reset time after lockout in minutes (default: 30)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "bad lockout attempt"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.65 NAME 'sambaLockoutThreshold'
|
||||||
|
DESC 'Lockout users after bad logon attempts (default: 0 => off)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "disconnect time"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.66 NAME 'sambaForceLogoff'
|
||||||
|
DESC 'Disconnect Users outside logon hours (default: -1 => off, 0 => on)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
# "refuse machine password change"
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.67 NAME 'sambaRefuseMachinePwdChange'
|
||||||
|
DESC 'Allow Machine Password changes (default: 0 => off)'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.68 NAME 'sambaClearTextPassword'
|
||||||
|
DESC 'Clear text password (used for trusted domain passwords)'
|
||||||
|
EQUALITY octetStringMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||||
|
|
||||||
|
#
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.69 NAME 'sambaPreviousClearTextPassword'
|
||||||
|
DESC 'Previous clear text password (used for trusted domain passwords)'
|
||||||
|
EQUALITY octetStringMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.70 NAME 'sambaTrustType'
|
||||||
|
DESC 'Type of trust'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.71 NAME 'sambaTrustAttributes'
|
||||||
|
DESC 'Trust attributes for a trusted domain'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.72 NAME 'sambaTrustDirection'
|
||||||
|
DESC 'Direction of a trust'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.73 NAME 'sambaTrustPartner'
|
||||||
|
DESC 'Fully qualified name of the domain with which a trust exists'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.74 NAME 'sambaFlatName'
|
||||||
|
DESC 'NetBIOS name of a domain'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.75 NAME 'sambaTrustAuthOutgoing'
|
||||||
|
DESC 'Authentication information for the outgoing portion of a trust'
|
||||||
|
EQUALITY caseExactMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.76 NAME 'sambaTrustAuthIncoming'
|
||||||
|
DESC 'Authentication information for the incoming portion of a trust'
|
||||||
|
EQUALITY caseExactMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.77 NAME 'sambaSecurityIdentifier'
|
||||||
|
DESC 'SID of a trusted domain'
|
||||||
|
EQUALITY caseIgnoreIA5Match SUBSTR caseExactIA5SubstringsMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.78 NAME 'sambaTrustForestTrustInfo'
|
||||||
|
DESC 'Forest trust information for a trusted domain object'
|
||||||
|
EQUALITY caseExactMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.79 NAME 'sambaTrustPosixOffset'
|
||||||
|
DESC 'POSIX offset of a trust'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.7165.2.1.80 NAME 'sambaSupportedEncryptionTypes'
|
||||||
|
DESC 'Supported encryption types of a trust'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
## objectClasses used by Samba 3.0 schema ##
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
## The X.500 data model (and therefore LDAPv3) says that each entry can
|
||||||
|
## only have one structural objectclass. OpenLDAP 2.0 does not enforce
|
||||||
|
## this currently but will in v2.1
|
||||||
|
|
||||||
|
##
|
||||||
|
## added new objectclass (and OID) for 3.0 to help us deal with backwards
|
||||||
|
## compatibility with 2.2 installations (e.g. ldapsam_compat) --jerry
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' SUP top AUXILIARY
|
||||||
|
DESC 'Samba 3.0 Auxilary SAM Account'
|
||||||
|
MUST ( uid $ sambaSID )
|
||||||
|
MAY ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $
|
||||||
|
sambaLogonTime $ sambaLogoffTime $ sambaKickoffTime $
|
||||||
|
sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $
|
||||||
|
displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $
|
||||||
|
sambaProfilePath $ description $ sambaUserWorkstations $
|
||||||
|
sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
|
||||||
|
sambaBadPasswordCount $ sambaBadPasswordTime $
|
||||||
|
sambaPasswordHistory $ sambaLogonHours))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Group mapping info
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' SUP top AUXILIARY
|
||||||
|
DESC 'Samba Group Mapping'
|
||||||
|
MUST ( gidNumber $ sambaSID $ sambaGroupType )
|
||||||
|
MAY ( displayName $ description $ sambaSIDList ))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Trust password for trust relationships (any kind)
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.14 NAME 'sambaTrustPassword' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Trust Password'
|
||||||
|
MUST ( sambaDomainName $ sambaNTPassword $ sambaTrustFlags )
|
||||||
|
MAY ( sambaSID $ sambaPwdLastSet ))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Trust password for trusted domains
|
||||||
|
## (to be stored beneath the trusting sambaDomain object in the DIT)
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.15 NAME 'sambaTrustedDomainPassword' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Trusted Domain Password'
|
||||||
|
MUST ( sambaDomainName $ sambaSID $
|
||||||
|
sambaClearTextPassword $ sambaPwdLastSet )
|
||||||
|
MAY ( sambaPreviousClearTextPassword ))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Whole-of-domain info
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Domain Information'
|
||||||
|
MUST ( sambaDomainName $
|
||||||
|
sambaSID )
|
||||||
|
MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $
|
||||||
|
sambaAlgorithmicRidBase $
|
||||||
|
sambaMinPwdLength $ sambaPwdHistoryLength $ sambaLogonToChgPwd $
|
||||||
|
sambaMaxPwdAge $ sambaMinPwdAge $
|
||||||
|
sambaLockoutDuration $ sambaLockoutObservationWindow $ sambaLockoutThreshold $
|
||||||
|
sambaForceLogoff $ sambaRefuseMachinePwdChange ))
|
||||||
|
|
||||||
|
##
|
||||||
|
## used for idmap_ldap module
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.7 NAME 'sambaUnixIdPool' SUP top AUXILIARY
|
||||||
|
DESC 'Pool for allocating UNIX uids/gids'
|
||||||
|
MUST ( uidNumber $ gidNumber ) )
|
||||||
|
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.8 NAME 'sambaIdmapEntry' SUP top AUXILIARY
|
||||||
|
DESC 'Mapping from a SID to an ID'
|
||||||
|
MUST ( sambaSID )
|
||||||
|
MAY ( uidNumber $ gidNumber ) )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.9 NAME 'sambaSidEntry' SUP top STRUCTURAL
|
||||||
|
DESC 'Structural Class for a SID'
|
||||||
|
MUST ( sambaSID ) )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.10 NAME 'sambaConfig' SUP top AUXILIARY
|
||||||
|
DESC 'Samba Configuration Section'
|
||||||
|
MAY ( description ) )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.11 NAME 'sambaShare' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Share Section'
|
||||||
|
MUST ( sambaShareName )
|
||||||
|
MAY ( description ) )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.12 NAME 'sambaConfigOption' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Configuration Option'
|
||||||
|
MUST ( sambaOptionName )
|
||||||
|
MAY ( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $
|
||||||
|
sambaStringListoption $ description ) )
|
||||||
|
|
||||||
|
|
||||||
|
## retired during privilege rewrite
|
||||||
|
##objectclass ( 1.3.6.1.4.1.7165.2.2.13 NAME 'sambaPrivilege' SUP top AUXILIARY
|
||||||
|
## DESC 'Samba Privilege'
|
||||||
|
## MUST ( sambaSID )
|
||||||
|
## MAY ( sambaPrivilegeList ) )
|
||||||
|
|
||||||
|
##
|
||||||
|
## used for IPA_ldapsam
|
||||||
|
##
|
||||||
|
objectclass ( 1.3.6.1.4.1.7165.2.2.16 NAME 'sambaTrustedDomain' SUP top STRUCTURAL
|
||||||
|
DESC 'Samba Trusted Domain Object'
|
||||||
|
MUST ( cn )
|
||||||
|
MAY ( sambaTrustType $ sambaTrustAttributes $ sambaTrustDirection $
|
||||||
|
sambaTrustPartner $ sambaFlatName $ sambaTrustAuthOutgoing $
|
||||||
|
sambaTrustAuthIncoming $ sambaSecurityIdentifier $
|
||||||
|
sambaTrustForestTrustInfo $ sambaTrustPosixOffset $
|
||||||
|
sambaSupportedEncryptionTypes) )
|
166
test_utils/runner.py
Normal file
166
test_utils/runner.py
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""Defines the custom runners for Re2o.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import volatildap
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from django.test.runner import DiscoverRunner
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from users.models import LdapUser, LdapUserGroup, LdapServiceUser, LdapServiceUserGroup
|
||||||
|
|
||||||
|
# The path of this file
|
||||||
|
__here = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
# The absolute path where to find the schemas for the LDAP
|
||||||
|
schema_path = os.path.abspath(os.path.join(__here, 'ldap', 'schema'))
|
||||||
|
# The absolute path of the "radius.schema" file
|
||||||
|
radius_schema_path = os.path.join(schema_path, 'radius.schema')
|
||||||
|
# The absolute path of the "samba.schema" file
|
||||||
|
samba_schema_path = os.path.join(schema_path, 'samba.schema')
|
||||||
|
|
||||||
|
# The suffix for the LDAP
|
||||||
|
suffix = 'dc=example,dc=net'
|
||||||
|
# The admin CN of the LDAP
|
||||||
|
rootdn = 'cn=admin,'+suffix
|
||||||
|
|
||||||
|
# Defines all ldap_entry mandatory for Re2o under a key-value list format
|
||||||
|
# that can be used directly by volatildap. For more on how to generate this
|
||||||
|
# data, see https://gitlab.federez.net/re2o/scripts/blob/master/print_ldap_entries.py
|
||||||
|
ldapentry_Utilisateurs = ('cn=Utilisateurs,'+suffix, {
|
||||||
|
'cn': ['Utilisateurs'],
|
||||||
|
'sambaSID': ['500'],
|
||||||
|
'uid': ['Users'],
|
||||||
|
'objectClass': ['posixGroup', 'top', 'sambaSamAccount', 'radiusprofile'],
|
||||||
|
'gidNumber': ['500'],
|
||||||
|
})
|
||||||
|
ldapentry_groups = ('ou=groups,'+suffix, {
|
||||||
|
'ou': ['groups'],
|
||||||
|
'objectClass': ['organizationalUnit'],
|
||||||
|
'description': ["Groupes d'utilisateurs"],
|
||||||
|
})
|
||||||
|
ldapentry_services = ('ou=services,ou=groups,'+suffix, {
|
||||||
|
'ou': ['services'],
|
||||||
|
'objectClass': ['organizationalUnit'],
|
||||||
|
'description': ['Groupes de comptes techniques'],
|
||||||
|
})
|
||||||
|
ldapentry_service_users = ('ou=service-users,'+suffix, {
|
||||||
|
'ou': ['service-users'],
|
||||||
|
'objectClass': ['organizationalUnit'],
|
||||||
|
'description': ["Utilisateurs techniques de l'annuaire"],
|
||||||
|
})
|
||||||
|
ldapentry_freeradius = ('cn=freeradius,ou=service-users,'+suffix, {
|
||||||
|
'cn': ['freeradius'],
|
||||||
|
'objectClass': ['applicationProcess', 'simpleSecurityObject'],
|
||||||
|
'userPassword': ['FILL_IT'],
|
||||||
|
})
|
||||||
|
ldapentry_nssauth = ('cn=nssauth,ou=service-users,'+suffix, {
|
||||||
|
'cn': ['nssauth'],
|
||||||
|
'objectClass': ['applicationProcess', 'simpleSecurityObject'],
|
||||||
|
'userPassword': ['FILL_IT'],
|
||||||
|
})
|
||||||
|
ldapentry_auth = ('cn=auth,ou=services,ou=groups,'+suffix, {
|
||||||
|
'cn': ['auth'],
|
||||||
|
'objectClass': ['groupOfNames'],
|
||||||
|
'member': ['cn=nssauth,ou=service-users,'+suffix],
|
||||||
|
})
|
||||||
|
ldapentry_posix = ('ou=posix,ou=groups,'+suffix, {
|
||||||
|
'ou': ['posix'],
|
||||||
|
'objectClass': ['organizationalUnit'],
|
||||||
|
'description': ['Groupes de comptes POSIX'],
|
||||||
|
})
|
||||||
|
ldapentry_wifi = ('cn=wifi,ou=service-users,'+suffix, {
|
||||||
|
'cn': ['wifi'],
|
||||||
|
'objectClass': ['applicationProcess', 'simpleSecurityObject'],
|
||||||
|
'userPassword': ['FILL_IT'],
|
||||||
|
})
|
||||||
|
ldapentry_usermgmt = ('cn=usermgmt,ou=services,ou=groups,'+suffix, {
|
||||||
|
'cn': ['usermgmt'],
|
||||||
|
'objectClass': ['groupOfNames'],
|
||||||
|
'member': ['cn=wifi,ou=service-users,'+suffix],
|
||||||
|
})
|
||||||
|
ldapentry_replica = ('cn=replica,ou=service-users,'+suffix, {
|
||||||
|
'cn': ['replica'],
|
||||||
|
'objectClass': ['applicationProcess', 'simpleSecurityObject'],
|
||||||
|
'userPassword': ['FILL_IT'],
|
||||||
|
})
|
||||||
|
ldapentry_readonly = ('cn=readonly,ou=services,ou=groups,'+suffix, {
|
||||||
|
'cn': ['readonly'],
|
||||||
|
'objectClass': ['groupOfNames'],
|
||||||
|
'member': ['cn=replica,ou=service-users,'+suffix, 'cn=freeradius,ou=service-users,'+suffix],
|
||||||
|
})
|
||||||
|
ldapbasic = dict([ldapentry_Utilisateurs, ldapentry_groups,
|
||||||
|
ldapentry_services, ldapentry_service_users,
|
||||||
|
ldapentry_freeradius, ldapentry_nssauth, ldapentry_auth,
|
||||||
|
ldapentry_posix, ldapentry_wifi, ldapentry_usermgmt,
|
||||||
|
ldapentry_replica, ldapentry_readonly])
|
||||||
|
|
||||||
|
|
||||||
|
class DiscoverLdapRunner(DiscoverRunner):
|
||||||
|
"""Discovers all the tests in the project
|
||||||
|
|
||||||
|
This is a simple subclass of the default test runner
|
||||||
|
`django.test.runner.DiscoverRunner` that creates a test LDAP
|
||||||
|
right after the test databases are setup and destroys it right
|
||||||
|
before the test databases are setup.
|
||||||
|
It also ensure re2o's settings are using this new LDAP.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The `volatildap.LdapServer` instance initiated with the minimal
|
||||||
|
# structure required by Re2o
|
||||||
|
ldap_server = volatildap.LdapServer(
|
||||||
|
suffix=suffix,
|
||||||
|
rootdn=rootdn,
|
||||||
|
initial_data=ldapbasic,
|
||||||
|
schemas=['core.schema', 'cosine.schema', 'inetorgperson.schema',
|
||||||
|
'nis.schema', radius_schema_path, samba_schema_path]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
settings.DATABASES['ldap']['USER'] = self.ldap_server.rootdn
|
||||||
|
settings.DATABASES['ldap']['PASSWORD'] = self.ldap_server.rootpw
|
||||||
|
settings.DATABASES['ldap']['NAME'] = self.ldap_server.uri
|
||||||
|
settings.LDAP['base_user_dn'] = ldapentry_Utilisateurs[0]
|
||||||
|
settings.LDAP['base_userservice_dn'] = ldapentry_service_users[0]
|
||||||
|
settings.LDAP['base_usergroup_dn'] = ldapentry_posix[0]
|
||||||
|
settings.LDAP['base_userservicegroup_dn'] = ldapentry_services[0]
|
||||||
|
settings.LDAP['user_gid'] = ldapentry_Utilisateurs[1].get('gidNumber', ["500"])[0]
|
||||||
|
LdapUser.base_dn = settings.LDAP['base_user_dn']
|
||||||
|
LdapUserGroup.base_dn = settings.LDAP['base_usergroup_dn']
|
||||||
|
LdapServiceUser.base_dn = settings.LDAP['base_userservice_dn']
|
||||||
|
LdapServiceUserGroup.base_dn = settings.LDAP['base_userservicegroup_dn']
|
||||||
|
super(DiscoverLdapRunner, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_databases(self, *args, **kwargs):
|
||||||
|
ret = super(DiscoverLdapRunner, self).setup_databases(*args, **kwargs)
|
||||||
|
print("Creating test LDAP with volatildap...")
|
||||||
|
self.ldap_server.start()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def teardown_databases(self, *args, **kwargs):
|
||||||
|
self.ldap_server.stop()
|
||||||
|
print("Destroying test LDAP...")
|
||||||
|
super(DiscoverLdapRunner, self).teardown_databases(*args, **kwargs)
|
||||||
|
|
|
@ -23,6 +23,65 @@
|
||||||
The tests for the Users module.
|
The tests for the Users module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# from django.test import TestCase
|
import os.path
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.conf import settings
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
import volatildap
|
||||||
|
|
||||||
|
|
||||||
|
class SchoolTestCase(TestCase):
|
||||||
|
def test_school_are_created(self):
|
||||||
|
s = models.School.objects.create(name="My awesome school")
|
||||||
|
self.assertEqual(s.name, "My awesome school")
|
||||||
|
|
||||||
|
|
||||||
|
class ListShellTestCase(TestCase):
|
||||||
|
def test_shell_are_created(self):
|
||||||
|
s = models.ListShell.objects.create(shell="/bin/zsh")
|
||||||
|
self.assertEqual(s.shell, "/bin/zsh")
|
||||||
|
|
||||||
|
|
||||||
|
class LdapUserTestCase(TestCase):
|
||||||
|
def test_create_ldap_user(self):
|
||||||
|
g = models.LdapUser.objects.create(
|
||||||
|
gid="500",
|
||||||
|
name="users_test_ldapuser",
|
||||||
|
uid="users_test_ldapuser",
|
||||||
|
uidNumber="21001",
|
||||||
|
sn="users_test_ldapuser",
|
||||||
|
login_shell="/bin/false",
|
||||||
|
mail="user@example.net",
|
||||||
|
given_name="users_test_ldapuser",
|
||||||
|
home_directory="/home/moamoak",
|
||||||
|
display_name="users_test_ldapuser",
|
||||||
|
dialupAccess="False",
|
||||||
|
sambaSID="21001",
|
||||||
|
user_password="{SSHA}aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
|
||||||
|
sambat_nt_password="0123456789ABCDEF0123456789ABCDEF",
|
||||||
|
macs=[],
|
||||||
|
shadowexpire="0"
|
||||||
|
)
|
||||||
|
self.assertEqual(g.name, 'users_test_ldapuser')
|
||||||
|
|
||||||
|
|
||||||
|
class LdapUserGroupTestCase(TestCase):
|
||||||
|
def test_create_ldap_user_group(self):
|
||||||
|
g = models.LdapUserGroup.objects.create(
|
||||||
|
gid="501",
|
||||||
|
members=[],
|
||||||
|
name="users_test_ldapusergroup"
|
||||||
|
)
|
||||||
|
self.assertEqual(g.name, 'users_test_ldapusergroup')
|
||||||
|
|
||||||
|
|
||||||
|
class LdapServiceUserTestCase(TestCase):
|
||||||
|
def test_create_ldap_service_user(self):
|
||||||
|
g = models.LdapServiceUser.objects.create(
|
||||||
|
name="users_test_ldapserviceuser",
|
||||||
|
user_password="{SSHA}AbCdEfGhIjKlMnOpQrStUvWxYz987654"
|
||||||
|
)
|
||||||
|
self.assertEqual(g.name, 'users_test_ldapserviceuser')
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
|
Loading…
Reference in a new issue