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
--- 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
--- 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')
--- 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")
--- /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)
+ ]
--- 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)
--- /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
--- 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': {
--- 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