From f78f575f8f3772dad8b61033cc676b52f339c1c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Yrj=C3=B6l=C3=A4?= Date: Thu, 11 Jun 2015 20:55:36 +0300 Subject: [PATCH] Many improvements --- {hkiprofile => hkijwt}/__init__.py | 0 {hkiprofile => hkijwt}/admin.py | 0 hkijwt/migrations/0001_initial.py | 23 ++++++++++++ {hkiprofile => hkijwt}/migrations/__init__.py | 0 hkijwt/models.py | 7 ++++ {hkiprofile => hkijwt}/tests.py | 0 {hkiprofile => hkijwt}/views.py | 0 hkiprofile/models.py | 17 --------- hkisaml/api.py | 37 +++++++++++++++++-- hkisaml/settings.py | 22 ++++++++--- requirements.txt | 1 + users/__init__.py | 0 users/admin.py | 3 ++ .../migrations/0001_initial.py | 2 - users/migrations/__init__.py | 0 users/models.py | 12 ++++++ users/tests.py | 3 ++ users/views.py | 3 ++ 18 files changed, 102 insertions(+), 28 deletions(-) rename {hkiprofile => hkijwt}/__init__.py (100%) rename {hkiprofile => hkijwt}/admin.py (100%) create mode 100644 hkijwt/migrations/0001_initial.py rename {hkiprofile => hkijwt}/migrations/__init__.py (100%) create mode 100644 hkijwt/models.py rename {hkiprofile => hkijwt}/tests.py (100%) rename {hkiprofile => hkijwt}/views.py (100%) delete mode 100644 hkiprofile/models.py create mode 100644 users/__init__.py create mode 100644 users/admin.py rename {hkiprofile => users}/migrations/0001_initial.py (97%) create mode 100644 users/migrations/__init__.py create mode 100644 users/models.py create mode 100644 users/tests.py create mode 100644 users/views.py diff --git a/hkiprofile/__init__.py b/hkijwt/__init__.py similarity index 100% rename from hkiprofile/__init__.py rename to hkijwt/__init__.py diff --git a/hkiprofile/admin.py b/hkijwt/admin.py similarity index 100% rename from hkiprofile/admin.py rename to hkijwt/admin.py diff --git a/hkijwt/migrations/0001_initial.py b/hkijwt/migrations/0001_initial.py new file mode 100644 index 00000000..ea2d73c3 --- /dev/null +++ b/hkijwt/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AppToAppPermission', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('requester', models.ForeignKey(related_name='+', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('target', models.ForeignKey(related_name='+', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ], + ), + ] diff --git a/hkiprofile/migrations/__init__.py b/hkijwt/migrations/__init__.py similarity index 100% rename from hkiprofile/migrations/__init__.py rename to hkijwt/migrations/__init__.py diff --git a/hkijwt/models.py b/hkijwt/models.py new file mode 100644 index 00000000..ac1a766b --- /dev/null +++ b/hkijwt/models.py @@ -0,0 +1,7 @@ +from django.db import models +from oauth2_provider.models import Application + + +class AppToAppPermission(models.Model): + requester = models.ForeignKey(Application, db_index=True, related_name='+') + target = models.ForeignKey(Application, db_index=True, related_name='+') diff --git a/hkiprofile/tests.py b/hkijwt/tests.py similarity index 100% rename from hkiprofile/tests.py rename to hkijwt/tests.py diff --git a/hkiprofile/views.py b/hkijwt/views.py similarity index 100% rename from hkiprofile/views.py rename to hkijwt/views.py diff --git a/hkiprofile/models.py b/hkiprofile/models.py deleted file mode 100644 index 47574f3f..00000000 --- a/hkiprofile/models.py +++ /dev/null @@ -1,17 +0,0 @@ -import uuid -import logging -from django.db import models -from django.contrib.auth.models import AbstractUser - -logger = logging.getLogger(__name__) - - -class User(AbstractUser): - uuid = models.UUIDField(primary_key=True) - department_name = models.CharField(max_length=50, null=True, blank=True) - primary_sid = models.CharField(max_length=100, unique=True) - - def save(self, *args, **kwargs): - if self.uuid is None: - self.uuid = uuid.uuid1() - return super(User, self).save(*args, **kwargs) diff --git a/hkisaml/api.py b/hkisaml/api.py index 30a0b67a..703e252f 100644 --- a/hkisaml/api.py +++ b/hkisaml/api.py @@ -1,9 +1,16 @@ +import logging import jwt +import datetime from django.contrib.auth import get_user_model from rest_framework import permissions, serializers, generics, mixins, views from rest_framework.response import Response +from rest_framework.exceptions import PermissionDenied from oauth2_provider.ext.rest_framework import TokenHasReadWriteScope +from oauth2_provider.models import get_application_model +from hkijwt.models import AppToAppPermission + +logger = logging.getLogger(__name__) class UserSerializer(serializers.ModelSerializer): @@ -46,11 +53,35 @@ def get_object(self): class GetJWTView(views.APIView): - # permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope] + permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope] + def get(self, request, format=None): - secret = '12345' - user = get_user_model().objects.first() + requester_app = request.auth.application + target_app = request.QUERY_PARAMS.get('target_app', '').strip() + if target_app: + qs = get_application_model().objects.all() + target_app = generics.get_object_or_404(qs, client_id=target_app) + try: + perm = AppToAppPermission.objects.get(requester=requester_app, + target=target_app) + except AppToAppPermission.DoesNotExist: + raise PermissionDenied() + else: + target_app = requester_app + + secret = target_app.client_secret + user = request.user + payload = UserSerializer(user).data + delete_fields = ['last_login', 'date_joined', 'uuid'] + for field in delete_fields: + if field in payload: + del payload[field] + + payload['iss'] = 'https://api.hel.fi/sso' # FIXME: Make configurable + payload['sub'] = str(user.uuid) + payload['aud'] = target_app.client_id + payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=60) encoded = jwt.encode(payload, secret, algorithm='HS256') return Response({'token': encoded}) diff --git a/hkisaml/settings.py b/hkisaml/settings.py index 10bafa08..5025ff1d 100644 --- a/hkisaml/settings.py +++ b/hkisaml/settings.py @@ -35,10 +35,15 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', 'djangosaml2', 'oauth2_provider', 'corsheaders', - 'hkiprofile', + + 'helusers', + + 'hkijwt', + 'users', ) MIDDLEWARE_CLASSES = ( @@ -87,7 +92,7 @@ LOGIN_URL = '/sso/saml2/login/' SESSION_EXPIRE_AT_BROWSER_CLOSE = True -AUTH_USER_MODEL = 'hkiprofile.User' +AUTH_USER_MODEL = 'users.User' # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ @@ -187,9 +192,9 @@ 'contact_type': 'technical' }], # you can set multilanguage information here - #'organization': { + # 'organization': { # 'name': [('City of Helsinki', 'en'), ('Helsingin kaupunki', 'fi')], - #}, + # }, 'valid_for': 24, # how long is our metadata valid } @@ -210,12 +215,12 @@ 'level': 'DEBUG', 'class': 'django.utils.log.NullHandler', }, - 'console':{ + 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, - }, + }, 'loggers': { 'django': { 'handlers': ['console'], @@ -249,6 +254,11 @@ CORS_ORIGIN_ALLOW_ALL = True +OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' +OAUTH2_PROVIDER = { + 'CLIENT_SECRET_GENERATOR_LENGTH': 96, +} + REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'oauth2_provider.ext.rest_framework.OAuth2Authentication', diff --git a/requirements.txt b/requirements.txt index 490db9ae..26fe4268 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ django-oauth-toolkit django-cors-headers djangorestframework pyjwt +git+https://github.com/City-of-Helsinki/django-helusers.git#egg=django-helusers diff --git a/users/__init__.py b/users/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/users/admin.py b/users/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/users/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/hkiprofile/migrations/0001_initial.py b/users/migrations/0001_initial.py similarity index 97% rename from hkiprofile/migrations/0001_initial.py rename to users/migrations/0001_initial.py index 29830f5c..0084d00c 100644 --- a/hkiprofile/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -35,8 +35,6 @@ class Migration(migrations.Migration): ], options={ 'abstract': False, - 'verbose_name': 'user', - 'verbose_name_plural': 'users', }, managers=[ ('objects', django.contrib.auth.models.UserManager()), diff --git a/users/migrations/__init__.py b/users/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/users/models.py b/users/models.py new file mode 100644 index 00000000..3f0a0130 --- /dev/null +++ b/users/models.py @@ -0,0 +1,12 @@ +import uuid +from django.db import models +from helusers.models import AbstractUser + + +class User(AbstractUser): + primary_sid = models.CharField(max_length=100, unique=True) + + def save(self, *args, **kwargs): + if not self.primary_sid: + self.primary_sid = uuid.uuid4() + return super(User, self).save(*args, **kwargs) diff --git a/users/tests.py b/users/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/users/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/users/views.py b/users/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/users/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here.