diff --git a/.gitignore b/.gitignore index 11614af2870733183efe883810764d8708bddf8f..cabeafb73e333236e9deb83501b3a24bb0bfc5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,13 @@ dmypy.json # Pyre type checker .pyre/ +Pipfile* +django_config/ +manage.py + +# vim +*swp + +# reports for SonarQube +xunit-reports/ +coverage-reports/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f90a08c5d0ce41575f4383ba3cccaf8462834c08 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,42 @@ + +image: + name: python:3.5 + +variables: + SONAR_TOKEN: "5b1ac2b019175e23aeb4b5618e50893d0db78f1f" + SONAR_PROJECTKEY: "ideo-bfc/idgo_ows_accounts_manager" + SONAR_HOST_URL: "https://sonarqube.neogeo.fr" + GIT_DEPTH: 0 + +stages: + - test1 + - test2 + +test_unit: + stage: test1 + before_script: + - pip install -r requirements.txt + - pip install 'django<2.0.0' + - pip install pytest pytest-django pytest-cov + script: + - pytest --junitxml=xunit-reports/xunit-result-pytest.xml --cov-report xml:coverage-reports/coverage-pytest.xml --cov idgo_ows_account_manager + artifacts: + paths: + - coverage-reports/ + - xunit-reports/ + expire_in: 1 week + + +sonarqube-check: + image: + name: sonarsource/sonar-scanner-cli:latest + entrypoint: [""] + stage: test2 + script: + - sonar-scanner -Dsonar.qualitygate.wait=true -Dsonar.projectKey=$CI_PROJECT_NAME -Dsonar.projectName=$CI_PROJECT_NAME -Dsonar.projectVersion=$CI_COMMIT_BRANCH + allow_failure: true + dependencies: + - test_unit + only: + - develop + - master diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..e18d6573e499e87f8d01c53f28c3c889e0fd63f2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# CHANGELOG +Tous les changements notables apportés à ce projet seront documentés dans ce dossier. + +--- + +## TABLE DES MATIÈRES + - [Reste à réaliser](#Reste-à -réaliser) + - [Versions](#Versions) + - [[0.1.0] - Version initiale](#[0.1.0]---Version-initiale) + +--- + +## Reste à réaliser + +## Versions + +### [0.1.0] - Version initiale + +* Ajout script OGC compatible tokens +* Ajout d'une option de configuration dans le profile utilisateur pour créer un token personnalisé. diff --git a/README.md b/README.md index 42e7faa97b9b5adbf5cf042d3eec5a3df9b6e9d9..f13b80da6e52f38657dc3a40a413119c88ebd1db 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,87 @@ # IDGO WMS Token -Gestion des token pour l'accès à Mapserver en utilisant le protocole WMS \ No newline at end of file +Gestion des token pour l'accès à Mapserver en utilisant le protocole WMS + +--- + +## TABLE DES MATIÈRES + - [TABLE DES MATIÈRES](#TABLE-DES-MATIÈRES) + - [Installation](#Installation) + - [Configuration](#Configuration) + - [Utilisation](#Utilisation) + - [Démarrage des dockers](#Démarrage-des-dockers) + - [Arrêt des dockers](#Arrêt-des-dockers) + - [Versions](#Versions) + - [Auteurs](#Auteurs) + +--- + +## Installation + +C'est un plugin pour idgo, donc installer idgo avant ! + +``` +pip install git+https://@gitlab.neogeo.fr/ideo-bfc/idgo_ows_accounts_manager.git@master#egg=idgo_ows_accounts_manager +``` + +## Configuration + +Pour activer l'application django, et activer le `context_processor` chargeant tous les templates de modification de profil utilisateur : +Dans le settings.py: + +``` +ACCOUNT_MANAGER_APPS = [ + 'idgo_ows_account_manager', +] + +INSTALLED_APPS = [ +[...] +] + RESOURCE_APPS + ACCOUNT_MANAGER_APPS + + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ +[...] + 'idgo_ows_account_manager.context_processors.list_account_manager_extensions', + ], + }, + }, +] + +DISABLE_ACCOUNT_PASSWORD = True +``` + +`DISABLE_ACCOUNT_PASSWORD` permet de ne pas afficher le mot de passe utilisateur + +Pour charger les 3 vues (pour creer, supprimer, changer le mot de passe) dans urls.py: +``` +for app in settings.RESOURCE_APPS + settings.ACCOUNT_MANAGER_APPS: + # on insert apres idgo_admin + urlpatterns.insert(2, url(r'^', include('{}.urls'.format(app), namespace=app)),) +``` + +Pour que le script OGC utilise en basic auth le username en nom d'utilisateur et le token en mot de passe. + + + +## Utilisation + +### Configurer son token + +Dans la nouvelle section "Compte WMS" du profil utilisateur, choisir "Créer un compte WMS", le login / mot de passe apparaitra, ainsi que les boutons pour supprimer le compte et regéner le mot de passe. + +### Utiliser son token + +Dans QGis, rensignier le mot de passe et le mot de passe dans la connexion WMS/WMTS/WFS, l'URL à utiliser est https://[qualif-]/[preprod-]/[]ogc.ternum-bfc.fr/ + +## Versions + +Voir le fichier [CHANGELOG](CHANGELOG.md) + +## Auteurs + * Néogéo Technologies diff --git a/idgo_admin_mock/__init__.py b/idgo_admin_mock/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/idgo_admin_mock/admin.py b/idgo_admin_mock/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/idgo_admin_mock/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/idgo_admin_mock/apps.py b/idgo_admin_mock/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..62d5b2c84c38f124fba4a85d60b7389c539093ff --- /dev/null +++ b/idgo_admin_mock/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class IdgoAdminMockConfig(AppConfig): + name = 'idgo_admin_mock' diff --git a/idgo_admin_mock/migrations/__init__.py b/idgo_admin_mock/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/idgo_admin_mock/models.py b/idgo_admin_mock/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/idgo_admin_mock/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/idgo_admin_mock/tests.py b/idgo_admin_mock/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/idgo_admin_mock/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/idgo_admin_mock/urls.py b/idgo_admin_mock/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..967d94c422543b91fe4f5b4439450166fecd1516 --- /dev/null +++ b/idgo_admin_mock/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import url +from idgo_admin_mock import views + +urlpatterns = [ + url(r'^fake/', views.mock_view, name="update_account"), +] diff --git a/idgo_admin_mock/views.py b/idgo_admin_mock/views.py new file mode 100644 index 0000000000000000000000000000000000000000..968bc5580b330174fce0368d17f2e380675dd835 --- /dev/null +++ b/idgo_admin_mock/views.py @@ -0,0 +1,4 @@ +from django.http import HttpResponse + +def mock_view(request): + return HttpResponse("OK") diff --git a/idgo_ows_account_manager/__init__.py b/idgo_ows_account_manager/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/idgo_ows_account_manager/admin.py b/idgo_ows_account_manager/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..5a9455e253010fd84a329a08b5484c9c09247ea4 --- /dev/null +++ b/idgo_ows_account_manager/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from idgo_ows_account_manager import models + +# Register your models here. +admin.site.register(models.WMSToken) diff --git a/idgo_ows_account_manager/apps.py b/idgo_ows_account_manager/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..78b8b6e762d704188632d46be7ea9ec2acd3a7dc --- /dev/null +++ b/idgo_ows_account_manager/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class IdgoOwsAccountManagerConfig(AppConfig): + name = 'idgo_ows_account_manager' diff --git a/idgo_ows_account_manager/auth_ogc.py b/idgo_ows_account_manager/auth_ogc.py new file mode 100755 index 0000000000000000000000000000000000000000..d9c6a91141fabf78292c6a35f9fbdf6c2c740923 --- /dev/null +++ b/idgo_ows_account_manager/auth_ogc.py @@ -0,0 +1,189 @@ +#!/WEBS/ternum/idgo.ternum.fr/docs/neogeo-idgo/bin/python3 +# Copyright (c) 2017-2020 Neogeo-Technologies. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import logging +import os +import sys +from urllib.parse import parse_qs +from urllib.parse import urlparse +python_home = "/WEBS/ternum/idgo.ternum.fr/docs/neogeo-idgo" +sys.path.append(python_home) + +# Obliqé pour WSGIAuthUserScript +activate_this = python_home + '/bin/activate_this.py' +with open(activate_this) as venv: + exec(venv.read(), {'__file__': activate_this}) + +from wl_proxy import load_proxy_environment +load_proxy_environment() +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_config.settings') +import django # noqa: E402 +django.setup() +from django.contrib.auth.models import User # noqa: E402 +from django.db.models import Q # noqa: E402 +from functools import reduce # noqa: E402 +from idgo_admin.models import Dataset # noqa: E402 +from idgo_admin.models import Organisation # noqa: E402 +from idgo_admin.models import Resource # noqa: E402 +from operator import ior # noqa: E402 + +from django.conf import settings + +if settings.DEBUG: + AUTH_SCRIPT_LOGFILE = '/FILER/idgo.ternum.fr/media/logos/auth-ogc.ternum.fr_error.log' + OTHER_LOGFILE = '/FILER/idgo.ternum.fr/media/logos/default.ternum.fr_error.log' + + logger = logging.getLogger('') + stream_handler = logging.FileHandler(OTHER_LOGFILE) + logger.addHandler(stream_handler) + logger.setLevel(logging.DEBUG) + + logger = logging.getLogger('auth_ogc') + stream_handler = logging.FileHandler(AUTH_SCRIPT_LOGFILE) + logger.addHandler(stream_handler) + logger.setLevel(logging.DEBUG) + + try: + if os.path.exists(OTHER_LOGFILE): + os.chmod(OTHER_LOGFILE, 0o666) + + if os.path.exists(AUTH_SCRIPT_LOGFILE): + os.chmod(AUTH_SCRIPT_LOGFILE, 0o666) + except: + pass +else: + logger = logging.getLogger('auth_ogc') + logger.setLevel(logging.WARN) + +AUTHORIZED_PREFIX = ['/maps/', '/wfs/', '/wms/', '/wxs/'] +# used for parsing address when basic auth is provided +PRIVATE_AUTHORIZED_PREFIX = ["/private{prefix}".format(prefix=p) + for p in AUTHORIZED_PREFIX] + + +def retrieve_resources_through_ows_url(url): + parsed_url = urlparse(url.lower()) + qs = parse_qs(parsed_url.query) + if 'layers' in qs: + layers = qs.get('layers')[-1] + elif 'typename' in qs: + layers = qs.get('typename')[-1] + elif 'typenames' in qs: + layers = qs.get('typenames')[-1] + else: + layers = None + if not layers: + return None + layers = set(layers.replace(' ', '').split(',')) + layers = [layer.split(':')[-1] for layer in layers] + datasets_filters = [ + Q(slug__in=layers), + Q(organisation__in=Organisation.objects.filter(slug__in=layers).distinct()), + ] + datasets = Dataset.objects.filter(reduce(ior, datasets_filters)).distinct() + resources_filters = [ + Q(dataset__in=datasets), + Q(layer__name__in=layers), + ] + resources = Resource.objects.filter(reduce(ior, resources_filters)).distinct() + return resources + + +def check_password(environ, user, password): + + url = environ['REQUEST_URI'] + + logger.debug('Checking user %s rights to url %s', user, url) + + # check path is authorized + + is_path_authorized = False + for prefix in AUTHORIZED_PREFIX + PRIVATE_AUTHORIZED_PREFIX: + if url.startswith(prefix): + is_path_authorized = True + + if not is_path_authorized: + logger.error("path '%s' is unauthorized", url) + return False + + # Get Capabilities and metadata are always athorized + qs = parse_qs(urlparse(url.lower()).query) + + request = qs.get('request') + logger.debug(qs) + public_requests = [ + "getcapabilities", + "getmetadata", + "getlegendgraphic", + "describefeaturetype", + "describelayer", + "getstyles", + ] + + if request[-1] in public_requests: + logger.debug("URL request is public") + return True + + try: + user = User.objects.get(username=user, is_active=True) + except User.DoesNotExist: + logger.debug("User %s does not exist (or is not active)" % user) + else: + if str(user.wmstoken.token) != password: + logger.error("User %s provided bad password", user) + #logger.debug(" '%s' instead of '%s'", password, user.wmstoken.token) + return False + + resources = retrieve_resources_through_ows_url(url) + if not resources: + logger.error("Unable to get resources") + return False + # Refuse query if one of the resources is not available/authorized + for resource in resources: + if resource.anonymous_access: + continue + if not resource.is_profile_authorized(user): + logger.error( + "Resource '{resource}' is not authorized to user '{user}'.".format( + resource=resource.pk, user=user.username)) + return False + return True + + +if __name__ == '__main__': + while True: + try: + line = sys.stdin.readline().strip() + logger.debug("REMAP ogc auth: %s" % line) + headers = {"REQUEST_URI": line} + # Remove querystring (handled by apache) + path = line.split("?")[0] + + # if ressource is accessible by anonymous => public, + # otherwise check password (=> private) + if check_password(headers, "", ""): + response = "http://localhost:8001/public{uri}".format(uri=path) + else: + response = "http://localhost:8001/private{uri}".format(uri=path) + + logger.debug("response : %s" % response) + sys.stdout.write(response + '\n') + sys.stdout.flush() + except Exception as e: + logger.error(e) + sys.stdout.write('NULL\n') + sys.stdout.flush() diff --git a/idgo_ows_account_manager/context_processors.py b/idgo_ows_account_manager/context_processors.py new file mode 100644 index 0000000000000000000000000000000000000000..bf23c769fe52b211688242447fa29531ca93bcb7 --- /dev/null +++ b/idgo_ows_account_manager/context_processors.py @@ -0,0 +1,33 @@ +from django.conf import settings + + +def list_account_manager_extensions(request): + """ + En considerant une liste regroupant les extensions de compte utilisateur: + ACCOUNT_MANAGER_APPS = ['idgo_ows_account_manager', ...] + INSTALLED_APPS = CORE_APPS + ... + ACCOUNT_MANAGER_APPS + + A charger dans les settings: + TEMPLATES = [ + { + # ... + 'OPTIONS': { + 'context_processors': [ + # ... + 'idgo_ows_account_manager.context_processors.list_account_manager_extensions' + ], + }, + }, + ] + Permet d'avoir la variable 'ACCOUNT_MANAGER_APPS' accessible depuis tous les + templates. + """ + + return { + 'ACCOUNT_MANAGER_APPS': [ + { + 'name': app, + 'template': '{}/account_manager_extent.html'.format(app) + } for app in getattr(settings, 'ACCOUNT_MANAGER_APPS', []) + ] + } diff --git a/idgo_ows_account_manager/migrations/0001_initial.py b/idgo_ows_account_manager/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..88d30e2f53af5e69534014bf5d39d5b3fb2ac1bd --- /dev/null +++ b/idgo_ows_account_manager/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.0 on 2020-03-12 15:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='WMSToken', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('token', models.UUIDField(default=uuid.uuid4)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/idgo_ows_account_manager/migrations/__init__.py b/idgo_ows_account_manager/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/idgo_ows_account_manager/models.py b/idgo_ows_account_manager/models.py new file mode 100644 index 0000000000000000000000000000000000000000..1b576a14c2c702239288abad3071ef6bd8e99dff --- /dev/null +++ b/idgo_ows_account_manager/models.py @@ -0,0 +1,14 @@ +import uuid + +from django.db import models +from django.contrib.auth import get_user_model + +User = get_user_model() + + +class WMSToken(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + token = models.UUIDField(default=uuid.uuid4) + + def __str__(self): + return self.user.username diff --git a/idgo_ows_account_manager/templates/idgo_ows_account_manager/account_manager_extent.html b/idgo_ows_account_manager/templates/idgo_ows_account_manager/account_manager_extent.html new file mode 100644 index 0000000000000000000000000000000000000000..33449930d35d6f95aa09c66e3bedc170001a2552 --- /dev/null +++ b/idgo_ows_account_manager/templates/idgo_ows_account_manager/account_manager_extent.html @@ -0,0 +1,12 @@ +<div class="well"> + <h4 class="modal-title">Compte WMS</h4> + <br /> +{% if not user.wmstoken %} + <p>Votre compte WMS n'est pas encore activé.</p> + <a class="btn btn-default" href="{% url "idgo_ows_account_manager:create_ows_account" %}">Créer mon compte WMS</a> +{% else %} + <p>Votre compte WMS est activé : <em>compte utilisateur</em> <strong>{{ user.username }}</strong> / <em>mot de passe</em> <strong>{{ user.wmstoken.token }}</strong></p> + <a class="btn btn-default" href="{% url "idgo_ows_account_manager:delete_ows_account" %}">Supprimer mon compte WMS</a></li> + <a class="btn btn-default" href="{% url "idgo_ows_account_manager:change_ows_password" %}">Générer nouveau mot de passe WMS</a></li> +{% endif %} +</div> diff --git a/idgo_ows_account_manager/tests.py b/idgo_ows_account_manager/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..36959992f4c3406f5fea8faae73dbc1389e8ae8e --- /dev/null +++ b/idgo_ows_account_manager/tests.py @@ -0,0 +1,79 @@ +from django.contrib.auth import get_user_model +from django.test import Client +from django.test import TestCase +from django.urls import reverse + +from idgo_ows_account_manager.models import WMSToken +from idgo_ows_account_manager.views import create_ows_account + +# Create your tests here. + +class TestViewsOWSAccount(TestCase): + + def teardown(self): + user = get_user_model() + WMSToken.objects.filter(user__username="sdarocha").delete() + user.objects.filter(username="sdarocha").delete() + + + def test_create_ows_account(self): + """ + Create a user without token + call create_ows_account + ensure his token is created + """ + user = get_user_model() + u = user.objects.create(username="sdarocha", password="123") + u.save() + + assert not WMSToken.objects.filter(user=u) + + c = Client() + c.force_login(u) + c.get(reverse('idgo_ows_account_manager:create_ows_account')) + + assert WMSToken.objects.get(user=u).token + + + def test_delete_ows_account(self): + """ + Create a user and his token + call delete_ows_account + ensure his token has disapeared + """ + user = get_user_model() + u = user.objects.create(username="sdarocha", password="123") + u.save() + assert not WMSToken.objects.filter(user=u) + + WMSToken.objects.create(user=u) + assert WMSToken.objects.get(user=u).token + + c = Client() + c.force_login(u) + c.get(reverse('idgo_ows_account_manager:delete_ows_account')) + + assert not WMSToken.objects.filter(user=u) + + + def test_change_ows_password(self): + """ + Create a user and his token + call change_ows_password + ensure token is different + """ + user = get_user_model() + u = user.objects.create(username="sdarocha", password="123") + u.save() + assert not WMSToken.objects.filter(user=u) + + WMSToken.objects.create(user=u) + token = WMSToken.objects.get(user=u).token + assert token + + c = Client() + c.force_login(u) + c.get(reverse('idgo_ows_account_manager:change_ows_password')) + + token2 = WMSToken.objects.get(user=u).token + assert token != token2 diff --git a/idgo_ows_account_manager/urls.py b/idgo_ows_account_manager/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..c367cc30574f7a930364d8d99cb7fe2c0e2bbdc0 --- /dev/null +++ b/idgo_ows_account_manager/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url, include + +from idgo_ows_account_manager.views import create_ows_account +from idgo_ows_account_manager.views import change_ows_password +from idgo_ows_account_manager.views import delete_ows_account + +urlpatterns = [ + url(r'^ows_account/create/', create_ows_account, name="create_ows_account"), + url(r'^ows_account/delete/', delete_ows_account, name="delete_ows_account"), + url(r'^ows_account/change/', change_ows_password, name="change_ows_password"), +] diff --git a/idgo_ows_account_manager/views.py b/idgo_ows_account_manager/views.py new file mode 100644 index 0000000000000000000000000000000000000000..8affa836033f614cf09a78a04d3bfe6d1bc03350 --- /dev/null +++ b/idgo_ows_account_manager/views.py @@ -0,0 +1,23 @@ +from django.contrib.auth import get_user_model +from django.shortcuts import redirect + +from idgo_ows_account_manager.models import WMSToken + +UPDATE_ACCOUNT_PAGE = 'idgo_admin:update_account' + + +def create_ows_account(request): + if not hasattr(request.user, "wmstoken"): + WMSToken.objects.create(user=request.user) + return redirect(UPDATE_ACCOUNT_PAGE) + + +def delete_ows_account(request): + WMSToken.objects.filter(user=request.user).delete() + return redirect(UPDATE_ACCOUNT_PAGE) + + +def change_ows_password(request): + WMSToken.objects.filter(user=request.user).delete() + WMSToken.objects.create(user=request.user) + return redirect(UPDATE_ACCOUNT_PAGE) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000000000000000000000000000000000000..f7e931aae4323418d9071ebb4fa50292d92bf418 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_config.settings") + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..5ee4d3346796be74b957ba3c87f58e57061d5a55 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +DJANGO_SETTINGS_MODULE = test_config.settings +python_files = tests.py test_*.py *_tests.py +junit_family=xunit1 + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..cb10be5467887222af28c7e0000643e0916bea9b --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup, find_packages + + +def get_requirements(): + with open('requirements.txt') as req_file: + reqs = req_file.readlines() + return [req for req in reqs if not req.startswith('-e')] + + +def get_long_description(): + with open('README.md', 'rb') as desc_file: + description = desc_file.read().decode('utf-8') + return description + + +setup( + name="idgo_ows_account_manager", + descrtiption="Plugin de gestion de token WMS", + packages=find_packages(), + long_description=get_long_description(), + url='https://gitlab.neogeo.fr/ideo-bfc/idgo_ows_accounts_manager', + install_requires=get_requirements() +) diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000000000000000000000000000000000000..d30cf6ac6a23c9574ebe9d6ef7363c3aed021855 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.sources = idgo_ows_account_manager/ +sonar.exclusions=**/*test* +sonar.tests = idgo_ows_account_manager/tests.py diff --git a/test_config/__init__.py b/test_config/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test_config/settings.py b/test_config/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..d6a2930a21c83c1def4ad59670acf32c19573f0a --- /dev/null +++ b/test_config/settings.py @@ -0,0 +1,122 @@ +""" +Django settings for test_config project. + +Generated by 'django-admin startproject' using Django 1.11.29. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'kv+a0nqwbf(9gr9&&j%6*oc+d_43y493k63=poha*yj49y2g&%' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +ACCOUNT_MANAGER_APPS = [ + 'idgo_ows_account_manager', +] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + ACCOUNT_MANAGER_APPS + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'test_config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + 'idgo_ows_account_manager.context_processors.list_account_manager_extensions', + ], + }, + }, +] + +WSGI_APPLICATION = 'test_config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + diff --git a/test_config/urls.py b/test_config/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..e75d5d9cc63a24d57d16eda940326641bccece36 --- /dev/null +++ b/test_config/urls.py @@ -0,0 +1,26 @@ +"""test_config URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url, include +from django.contrib import admin +from django.conf import settings + +urlpatterns = [ + url(r'^admin/', admin.site.urls), + url(r'^', include(('idgo_admin_mock.urls', 'idgo_admin_mock'), namespace='idgo_admin')), +] + +for app in settings.ACCOUNT_MANAGER_APPS: + urlpatterns.insert(2, url(r'^', include(('{}.urls'.format(app), app), namespace=app)),) diff --git a/test_config/wsgi.py b/test_config/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..f237f27c288126febb79350d1d4b647b4e365527 --- /dev/null +++ b/test_config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for test_config project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_config.settings") + +application = get_wsgi_application()