# HG changeset patch # User durandn # Date 1460386029 -7200 # Node ID 6bfac7c633a07c0893c04edbf3f09f0b8ec94554 # Parent 2d3dd0824e8c1d71040cc5c823fae87053676c4b Added permission management into metaeducation (instead of it being in renkanmanager) + more logging around auth code + corrected client.py for oauth server so it sends the correct content-type when using client credentials diff -r 2d3dd0824e8c -r 6bfac7c633a0 .settings/org.eclipse.core.resources.prefs --- a/.settings/org.eclipse.core.resources.prefs Thu Apr 21 16:26:30 2016 +0200 +++ b/.settings/org.eclipse.core.resources.prefs Mon Apr 11 16:47:09 2016 +0200 @@ -1,3 +1,6 @@ eclipse.preferences.version=1 encoding//oauth/oauth.py=utf-8 +encoding//server/src/metaeducation/migrations/0001_initial.py=utf-8 +encoding//server/src/metaeducation/migrations/0002_user_uai.py=utf-8 +encoding//server/src/metaeducation/migrations/0003_renkan_permissions.py=utf-8 encoding//server/src/metaeducation/settings/dev.py=utf-8 diff -r 2d3dd0824e8c -r 6bfac7c633a0 oauth/client.py --- a/oauth/client.py Thu Apr 21 16:26:30 2016 +0200 +++ b/oauth/client.py Mon Apr 11 16:47:09 2016 +0200 @@ -60,13 +60,14 @@ if 'remote_oauth_clientcredentials' in session: resp = requests.post( app.config["CREATE_RENKAN_ENDPOINT"]+"?act_as="+str(session.get("me_id", "anonymous")), - {"title": "RENKAN_FROM_GED"}, + json.dumps({"title": "RENKAN_FROM_GED"}), headers={ 'Authorization': 'Bearer %s' % session['remote_oauth_clientcredentials'][0], - 'renkan-act-as': session.get("me_id", "anonymous") + 'renkan-act-as': session.get("me_id", "anonymous"), + 'content-type': "application/json" } ) - print(resp.text) + print("%r : %r" %(resp.status_code, json.loads(resp.text))) return redirect('/') @app.route('/authorized') diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/middleware.py --- a/server/src/metaeducation/middleware.py Thu Apr 21 16:26:30 2016 +0200 +++ b/server/src/metaeducation/middleware.py Mon Apr 11 16:47:09 2016 +0200 @@ -11,19 +11,29 @@ if hasattr(settings, 'OAUTH_EXEMPT_URLS'): EXEMPT_URLS += [compile(expr) for expr in settings.OAUTH_EXEMPT_URLS] -class MtdcLoginRequiredWithContextMiddleware: +class MtdcLoginRequiredWithContextMiddleware(object): """ Middleware intended to emulate login_required decorator so we can forward the context query arg """ def process_request(self, request): + logger.debug("REQUEST: %r ", request.user) + logger.debug("REQUEST: Session - %r", request.session.get("OAUTH_CONTEXT_BASE_URL", "No OAuth Context in session")) + cached_user = getattr(request, "_cached_user", "None") + logger.debug("REQUEST: Cached user - %r", cached_user) if not request.user.is_authenticated(): path = request.path_info.lstrip('/') if not any(m.match(path) for m in EXEMPT_URLS): logger.debug("LOGIN_REQUIRED: User is not logged and this request triggered Oauth redirects") - if request.GET.get("context", ""): - context = request.GET["context"] + if request.GET.get("context", "") and not request.session.get("OAUTH_CONTEXT_BASE_URL", ""): + logger.debug("LOGIN_REQUIRED: no context in session, storing context") + logger.debug("LOGIN_REQUIRED: context queryarg is %r", request.GET.get("context", None)) + request.session["OAUTH_CONTEXT_BASE_URL"] = request.GET["context"] + context = request.session.get("OAUTH_CONTEXT_BASE_URL", "") + if context: response = redirect(settings.LOGIN_URL) logger.debug("LOGIN_REQUIRED: will redirect to %r", settings.LOGIN_URL) logger.debug("LOGIN_REQUIRED: query args will be %r", {"context": context, "next": settings.BASE_URL+path}) response["LOCATION"] += "?"+urlencode({"context": context, "next": settings.BASE_URL+path}) return response + else: + logger.debug("LOGIN REQUIRED: User not authenticated, no context in session or queryarg, aborting") diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/migrations/0003_renkan_permissions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/metaeducation/migrations/0003_renkan_permissions.py Mon Apr 11 16:47:09 2016 +0200 @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-04-11 13:24 +from __future__ import unicode_literals + +from django.db import migrations +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission + +def fix_renkan_permissions(apps, schema_editor): + User = get_user_model() + for user in User.objects.all(): + for perm in ["view_renkan", "add_renkan", "change_renkan", "delete_renkan", "add_revision", "delete_revision"]: + if not user.has_perm(perm): + perm_obj = Permission.objects.get(codename=perm) + user.user_permissions.add(perm_obj) + user.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('metaeducation', '0002_user_uai'), + ] + + operations = [ + migrations.RunPython(fix_renkan_permissions) + ] diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/mtdc_oauth_provider/views.py --- a/server/src/metaeducation/mtdc_oauth_provider/views.py Thu Apr 21 16:26:30 2016 +0200 +++ b/server/src/metaeducation/mtdc_oauth_provider/views.py Mon Apr 11 16:47:09 2016 +0200 @@ -57,7 +57,7 @@ logger.debug("AUTHORIZATION CODE AUTH: login almost complete, checking if user %r exists", sociallogin.account.extra_data.get('external_id', 'NO_ID')) user = get_user_model().objects.get(external_id=sociallogin.account.extra_data.get('external_id', '')) # if user exists, connect the account to the existing account and login logger.debug("AUTHORIZATION CODE AUTH: user %r exists, connecting to existing account", sociallogin.account.extra_data.get('external_id', 'NO_ID')) - sociallogin.state['process'] = 'connect' + sociallogin.state['process'] = 'connect' perform_login(request, user, 'none') except get_user_model().DoesNotExist: logger.debug("AUTHORIZATION CODE AUTH: user %r does not exist", sociallogin.account.extra_data.get('external_id', 'NO_ID')) @@ -66,7 +66,7 @@ return super(MtdcOAuth2Adapter, self).get_login_redirect_url(self, request) def new_user(self, request, sociallogin): - if 'username' in sociallogin.account.extra_data: + if 'external_id' in sociallogin.account.extra_data: logger.debug("AUTHORIZATION CODE AUTH: checking if user %r exists to populate sociallogin", sociallogin.account.extra_data.get('external_id', 'NO_ID')) user_queryset = get_user_model().objects.filter(external_id=sociallogin.account.extra_data.get('external_id', ''), username=sociallogin.account.extra_data['username']) if user_queryset.exists(): @@ -80,7 +80,7 @@ user.uai = sociallogin.account.extra_data.get('uai', '') return user else: - logger.debug("AUTHORIZATION CODE AUTH: no username in extra data") + logger.debug("AUTHORIZATION CODE AUTH: no external_id in extra data") return get_user_model()() def populate_user(self, @@ -91,8 +91,11 @@ user = sociallogin.user user.username = username user.save() + view_permission = Permission.objects.get(codename="view_renkan") add_permission = Permission.objects.get(codename="add_renkan") - user.user_permissions.add(add_permission) + change_permission = Permission.objects.get(codename="change_renkan") + delete_permission = Permission.objects.get(codename="delete_renkan") + user.user_permissions.add(view_permission, add_permission, change_permission, delete_permission) return user def complete_login(self, request, app, token, **kwargs): @@ -102,8 +105,6 @@ extra_data = resp.json() logger.debug("AUTHORIZATION CODE AUTH: response extra_data: %r ", extra_data) - if request.session.get("OAUTH_CONTEXT_BASE_URL", None) is not None: - del request.session["OAUTH_CONTEXT_BASE_URL"] return self.get_provider().sociallogin_from_response(request, extra_data) diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/permissions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/metaeducation/permissions.py Mon Apr 11 16:47:09 2016 +0200 @@ -0,0 +1,15 @@ +from rest_framework import permissions + +class MtdcObjectPermissions(permissions.DjangoObjectPermissions): + """ + Similar to `DjangoObjectPermissions`, but adding 'view' permissions. + """ + perms_map = { + 'GET': ['%(app_label)s.view_%(model_name)s'], + 'OPTIONS': ['%(app_label)s.view_%(model_name)s'], + 'HEAD': ['%(app_label)s.view_%(model_name)s'], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } \ No newline at end of file diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/settings/__init__.py --- a/server/src/metaeducation/settings/__init__.py Thu Apr 21 16:26:30 2016 +0200 +++ b/server/src/metaeducation/settings/__init__.py Mon Apr 11 16:47:09 2016 +0200 @@ -52,7 +52,15 @@ 'metaeducation.auth.MtdcOAuth2ClientCredentialsAuthentication', 'rest_framework.authentication.BasicAuthentication', 'renkanmanager.auth.CsrfExemptSessionAuthentication' - ) + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticatedOrReadOnly', + 'metaeducation.permissions.MtdcObjectPermissions', + ), + 'DEFAULT_PARSER_CLASSES': ( + 'rest_framework.parsers.JSONParser', + ), + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' } MIDDLEWARE_CLASSES = ( @@ -119,6 +127,7 @@ DEFAULT_RENKAN_ICON = "" RENKAN_USER_DISPLAY_FIELD = 'external_id' +SOCIALACCOUNT_STORE_TOKENS = False SOCIALACCOUNT_ADAPTER = "metaeducation.mtdc_oauth_provider.views.MtdcOAuth2Adapter" SOCIALACCOUNT_PROVIDERS = { 'mtdc': { diff -r 2d3dd0824e8c -r 6bfac7c633a0 server/src/metaeducation/signals.py --- a/server/src/metaeducation/signals.py Thu Apr 21 16:26:30 2016 +0200 +++ b/server/src/metaeducation/signals.py Mon Apr 11 16:47:09 2016 +0200 @@ -1,9 +1,13 @@ +import requests, json, sys, logging + +from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.auth.signals import user_logged_in, user_logged_out +from django.core.urlresolvers import reverse from django.db.models.signals import post_save from django.dispatch import receiver -from django.contrib.auth import get_user_model -from django.conf import settings -from django.core.urlresolvers import reverse -import requests, json, sys, logging +from guardian.shortcuts import assign_perm + logger = logging.getLogger(__name__) @@ -45,6 +49,41 @@ ) logger.debug("REFERENCING RENKAN: response is %r", reference_response.status_code) +def assign_permission(sender, instance, created, **kwargs): + from renkanmanager.models import Renkan, Revision, Workspace + if sender == Renkan and created: + creator = instance.creator + logger.debug("SIGNALS: RENKAN WAS CREATED, ASSIGNING PERMS TO USER %r FOR RENKAN %r", instance.creator, instance.renkan_guid) + assign_perm('renkanmanager.view_renkan', creator, instance) + assign_perm('renkanamanger.change_renkan', creator, instance) + assign_perm('renkanmanager.delete_renkan', creator, instance) + if sender == Revision and created: + creator = instance.creator + logger.debug("SIGNALS: REVISION WAS CREATED, ASSIGNING PERMS TO USER %r", instance.creator) + assign_perm('renkanmanager.view_revision', creator, instance) + assign_perm('renkanmanager.delete_revision', creator, instance) + if sender == Workspace and created: + creator = instance.creator + logger.debug("SIGNALS: WORKSPACE WAS CREATED, ASSIGNING PERMS TO USER %r", instance.creator) + assign_perm('renkanmanager.view_workspace', creator, instance) + assign_perm('renkanmanager.change_workspace', creator, instance) + assign_perm('renkanmanager.delete_workspace', creator, instance) +def log_user_logged_in(sender, request, user, **kwargs): + logger.debug("LOGGING IN: user logged in: %r", user) + +def log_user_logged_out(sender, request, user, **kwargs): + logger.debug("LOGGING OUT: DJANGO: user logged out: %r", user) + +def token_updated(sender, instance, created, **kwargs): + from allauth.socialaccount.models import SocialToken + if sender == SocialToken: + logger.debug("TOKEN: SocialToken saved") + logger.debug("TOKEN: Token created: %r", created) + if not 'test' in sys.argv: post_save.connect(reference_created_renkan) +post_save.connect(token_updated) +post_save.connect(assign_permission) +user_logged_in.connect(log_user_logged_in) +user_logged_out.connect(log_user_logged_out) \ No newline at end of file