From: Yasin Date: Tue, 28 Jan 2014 10:04:20 +0000 (+0100) Subject: Merge branch 'master' of ssh://git.onelab.eu/git/myslice X-Git-Tag: myslice-0.3-0~13^2 X-Git-Url: http://git.onelab.eu/?p=unfold.git;a=commitdiff_plain;h=db29e23d289c0edd696b8c6445765a908d59058c;hp=091860329ef984b892d5c5a29836dff2201d876f Merge branch 'master' of ssh://git.onelab.eu/git/myslice --- diff --git a/README b/README index 58078467..900d8b4c 100644 --- a/README +++ b/README @@ -228,6 +228,12 @@ $python manage.py loaddata temp_data.json If your changes break your old schema this won't work - in which case tools like south or django evolution are great. + +Add a new model to the DB + +$python manage.py schemamigration --auto +$python manage.py migrate + ======== update django database to reflect changes in existing models with migration system (e.g., south) ========= As south is already installed , you just have to do: diff --git a/portal/actions.py b/portal/actions.py index c373a30c..8745cb52 100644 --- a/portal/actions.py +++ b/portal/actions.py @@ -48,7 +48,6 @@ def sfa_update_user(request, user_hrn, user_params): results = execute_query(request,query) return results - def sfa_add_slice(request, slice_params): query = Query.create('slice').set(slice_params).select('slice_hrn') results = execute_query(request, query) @@ -56,6 +55,13 @@ def sfa_add_slice(request, slice_params): raise Exception, "Could not create %s. Already exists ?" % slice_params['hrn'] return results +def sfa_add_user_to_slice(request, user_hrn, slice_params): + query = Query.update('slice').filter_by('user_hrn', '==', user_hrn).set(slice_params).select('slice_hrn') + results = execute_query(request, query) + if not results: + raise Exception, "Could not create %s. Already exists ?" % slice_params['hrn'] + return results + # Propose hrn def manifold_add_user(request, user_params): @@ -131,6 +137,7 @@ def make_request_slice(slice): request = {} request['type'] = 'slice' request['id'] = slice.id + request['user_email'] = slice.user_email request['timestamp'] = slice.created request['authority_hrn'] = slice.authority_hrn request['slice_name'] = slice.slice_name @@ -260,6 +267,7 @@ def portal_validate_request(wsgi_request, request_ids): # ignored in request: id, timestamp, number_of_nodes, type_of_nodes, purpose sfa_add_slice(wsgi_request, sfa_slice_params) + #sfa_add_user_to_slice(wsgi_request, user_hrn, sfa_slice_params) # XXX Remove from database diff --git a/portal/forms.py b/portal/forms.py index fc8d8c3f..afbf3442 100644 --- a/portal/forms.py +++ b/portal/forms.py @@ -28,6 +28,10 @@ from portal.models import PendingUser, PendingSlice from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.tokens import default_token_generator from django.contrib.auth import authenticate, get_user_model +# TODO: Remove these automated forms and use html templates and views like any other page ! +# ERROR ImportError: cannot import name UNUSABLE_PASSWORD +# XXX This is not compatible with Django 1.6.1 +# Ref: https://github.com/dot2code/varnish-bans-manager/issues/8 from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher from django.contrib.sites.models import get_current_site from django.utils.http import int_to_base36 diff --git a/portal/models.py b/portal/models.py index 1569146a..0de4ce09 100644 --- a/portal/models.py +++ b/portal/models.py @@ -66,8 +66,25 @@ class PendingUser(models.Model): created = models.DateTimeField(auto_now_add = True) # models.ForeignKey(Institution) +class PendingAuthority(models.Model): + site_name = models.TextField() + site_authority = models.TextField() + site_abbreviated_name = models.TextField() + site_url = models.TextField() + site_latitude = models.TextField() + site_longitude = models.TextField() + address_line1 = models.TextField() + address_line2 = models.TextField() + address_line3 = models.TextField() + address_city = models.TextField() + address_postalcode = models.TextField() + address_state = models.TextField() + address_country = models.TextField() + created = models.DateTimeField(auto_now_add = True) + class PendingSlice(models.Model): slice_name = models.TextField() + user_email = models.TextField() authority_hrn = models.TextField(null=True) number_of_nodes = models.TextField(default=0) type_of_nodes = models.TextField(default='NA') diff --git a/portal/slicerequestview.py b/portal/slicerequestview.py index 5b496079..7865428d 100644 --- a/portal/slicerequestview.py +++ b/portal/slicerequestview.py @@ -70,6 +70,7 @@ class SliceRequestView (LoginRequiredAutoLogoutView): } s = PendingSlice( slice_name = slice_name, + user_email = email, authority_hrn = authority_hrn, number_of_nodes = number_of_nodes, purpose = purpose diff --git a/portal/static/css/onelab_marko.css b/portal/static/css/onelab_marko.css index f08e8bff..be647a36 100644 --- a/portal/static/css/onelab_marko.css +++ b/portal/static/css/onelab_marko.css @@ -15,6 +15,10 @@ height: 100%; } +table { + color:white; +} + .container h1, .container h2 { color: #fff !important; } diff --git a/portal/static/css/registration.css b/portal/static/css/registration.css index 5fe83215..3f83359c 100644 --- a/portal/static/css/registration.css +++ b/portal/static/css/registration.css @@ -5,7 +5,7 @@ I've started to move over such stuff in onelab.css feel free to add to this collection */ -label.error { +.error { float: none; color: red; padding-left: .5em; diff --git a/portal/urls.py b/portal/urls.py index d1e088c3..8dc968ff 100644 --- a/portal/urls.py +++ b/portal/urls.py @@ -31,11 +31,12 @@ from portal.accountview import AccountView, account_process from portal.contactview import ContactView from portal.slicerequestview import SliceRequestView from portal.registrationview import RegistrationView +from portal.joinview import JoinView from portal.sliceview import SliceView +from portal.validationview import ValidatePendingView # hopefully these should move in dedicated source files too from portal.views import PresViewView, pres_view_static, pres_view_methods, pres_view_animation -from portal.views import ValidatePendingView from portal.django_passresetview import password_reset, password_reset_done, password_reset_confirm, password_reset_complete # DEPRECATED #named_register_forms = ( @@ -64,6 +65,7 @@ urlpatterns = patterns('', url(r'^slice/(?P[\w\.]+)/?$', SliceView.as_view(),name='slice'), url(r'^account/account_process/?$', account_process), url(r'^register/?$', RegistrationView.as_view(), name='registration'), + url(r'^join/?$', JoinView.as_view(), name='join'), url(r'^contact/?$', ContactView.as_view(), name='contact'), #url(r'^pass_reset/?$', PassResetView.as_view(), name='pass_rest'), # Slice request diff --git a/portal/validationview.py b/portal/validationview.py new file mode 100644 index 00000000..54246322 --- /dev/null +++ b/portal/validationview.py @@ -0,0 +1,226 @@ +# -*- coding: utf-8 -*- +# +# portal/views.py: views for the portal application +# This file is part of the Manifold project. +# +# Authors: +# Jordan Augé +# Mohammed Yasin Rahman +# Loic Baron +# Copyright 2013, UPMC Sorbonne Universités / LIP6 +# +# 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 3, 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; see the file COPYING. If not, write to the Free Software +# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import json + +from django.http import HttpResponseRedirect, HttpResponse +from django.shortcuts import render +from django.template.loader import render_to_string + +from unfold.loginrequired import FreeAccessView +from ui.topmenu import topmenu_items_live, the_user + +from portal.event import Event +# presview is put in observation for now +#from plugins.pres_view import PresView +from plugins.raw import Raw + +# these seem totally unused for now +#from portal.util import RegistrationView, ActivationView + +from portal.models import PendingUser, PendingSlice +from portal.actions import get_request_by_authority +from manifold.manifoldapi import execute_query +from manifold.core.query import Query +from unfold.page import Page + +class ValidatePendingView(FreeAccessView): + template_name = "validate_pending.html" + + def get_context_data(self, **kwargs): + # We might have slices on different registries with different user accounts + # We note that this portal could be specific to a given registry, to which we register users, but i'm not sure that simplifies things + # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn + + #messages.info(self.request, 'You have logged in') + page = Page(self.request) + + ctx_my_authorities = {} + ctx_delegation_authorities = {} + + + # The user need to be logged in + if the_user(self.request): + # Who can a PI validate: + # His own authorities + those he has credentials for. + # In MySlice we need to look at credentials also. + + + # XXX This will have to be asynchroneous. Need to implement barriers, + # for now it will be sufficient to have it working statically + + # get user_id to later on query accounts + # XXX Having real query plan on local tables would simplify all this + # XXX $user_email is still not available for local tables + #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id') + user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id') + user, = execute_query(self.request, user_query) + user_id = user['user_id'] + + # Query manifold to learn about available SFA platforms for more information + # In general we will at least have the portal + # For now we are considering all registries + all_authorities = [] + platform_ids = [] + sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type') + sfa_platforms = execute_query(self.request, sfa_platforms_query) + for sfa_platform in sfa_platforms: + print "SFA PLATFORM > ", sfa_platform['platform'] + if not 'auth_type' in sfa_platform: + continue + auth = sfa_platform['auth_type'] + if not auth in all_authorities: + all_authorities.append(auth) + platform_ids.append(sfa_platform['platform_id']) + + print "W: Hardcoding platform myslice" + # There has been a tweak on how new platforms are referencing a + # so-called 'myslice' platform for storing authentication tokens. + # XXX This has to be removed in final versions. + myslice_platforms_query = Query().get('local:platform').filter_by('platform', '==', 'myslice').select('platform_id') + myslice_platforms = execute_query(self.request, myslice_platforms_query) + if myslice_platforms: + myslice_platform, = myslice_platforms + platform_ids.append(myslice_platform['platform_id']) + + # We can check on which the user has authoritity credentials = PI rights + credential_authorities = set() + credential_authorities_expired = set() + + # User account on these registries + user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('auth_type', 'config') + user_accounts = execute_query(self.request, user_accounts_query) + #print "=" * 80 + #print user_accounts + #print "=" * 80 + for user_account in user_accounts: + + print "USER ACCOUNT", user_account + if user_account['auth_type'] == 'reference': + continue # we hardcoded the myslice platform... + + config = json.loads(user_account['config']) + creds = [] + print "CONFIG KEYS", config.keys() + if 'authority_credentials' in config: + print "***", config['authority_credentials'].keys() + for authority_hrn, credential in config['authority_credentials'].items(): + #if credential is not expired: + credential_authorities.add(authority_hrn) + #else + # credential_authorities_expired.add(authority_hrn) + if 'delegated_authority_credentials' in config: + print "***", config['delegated_authority_credentials'].keys() + for authority_hrn, credential in config['delegated_authority_credentials'].items(): + #if credential is not expired: + credential_authorities.add(authority_hrn) + #else + # credential_authorities_expired.add(authority_hrn) + + print 'credential_authorities =', credential_authorities + print 'credential_authorities_expired =', credential_authorities_expired + + # ** Where am I a PI ** + # For this we need to ask SFA (of all authorities) = PI function + pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') + pi_authorities_tmp = execute_query(self.request, pi_authorities_query) + pi_authorities = set() + for pa in pi_authorities_tmp: + pi_authorities |= set(pa['pi_authorities']) + + print "pi_authorities =", pi_authorities + + # My authorities + I have a credential + pi_credential_authorities = pi_authorities & credential_authorities + pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired + pi_expired_credential_authorities = pi_authorities & credential_authorities_expired + # Authorities I've been delegated PI rights + pi_delegation_credential_authorities = credential_authorities - pi_authorities + pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities + + print "pi_credential_authorities =", pi_credential_authorities + print "pi_no_credential_authorities =", pi_no_credential_authorities + print "pi_expired_credential_authorities =", pi_expired_credential_authorities + print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities + print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities + + # Summary intermediary + pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities + pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities + + print "--" + print "pi_my_authorities = ", pi_my_authorities + print "pi_delegation_authorities = ", pi_delegation_authorities + + # Summary all + queried_pending_authorities = pi_my_authorities | pi_delegation_authorities + print "----" + print "queried_pending_authorities = ", queried_pending_authorities + + requests = get_request_by_authority(queried_pending_authorities) + for request in requests: + auth_hrn = request['authority_hrn'] + + if auth_hrn in pi_my_authorities: + dest = ctx_my_authorities + + # define the css class + if auth_hrn in pi_credential_authorities: + request['allowed'] = 'allowed' + elif auth_hrn in pi_expired_credential_authorities: + request['allowed'] = 'expired' + else: # pi_no_credential_authorities + request['allowed'] = 'denied' + + elif auth_hrn in pi_delegation_authorities: + dest = ctx_delegation_authorities + + if auth_hrn in pi_delegation_credential_authorities: + request['allowed'] = 'allowed' + else: # pi_delegation_expired_authorities + request['allowed'] = 'expired' + + else: + continue + + if not auth_hrn in dest: + dest[auth_hrn] = [] + dest[auth_hrn].append(request) + + context = super(ValidatePendingView, self).get_context_data(**kwargs) + context['my_authorities'] = ctx_my_authorities + context['delegation_authorities'] = ctx_delegation_authorities + + # XXX This is repeated in all pages + # more general variables expected in the template + context['title'] = 'Test view that combines various plugins' + # the menu items on the top + context['topmenu_items'] = topmenu_items_live('Validation', page) + # so we can sho who is logged + context['username'] = the_user(self.request) + + # XXX We need to prepare the page for queries + #context.update(page.prelude_env()) + + return context diff --git a/portal/views.py b/portal/views.py index 8194ed88..9596b177 100644 --- a/portal/views.py +++ b/portal/views.py @@ -6,6 +6,7 @@ # Authors: # Jordan Augé # Mohammed Yasin Rahman +# Loic Baron # Copyright 2013, UPMC Sorbonne Universités / LIP6 # # This program is free software; you can redistribute it and/or modify it under @@ -221,183 +222,3 @@ def pres_view_static(request, constraints, id): json_answer = json.dumps(cmd) return HttpResponse (json_answer, mimetype="application/json") - -class ValidatePendingView(FreeAccessView): - template_name = "validate_pending.html" - - def get_context_data(self, **kwargs): - # We might have slices on different registries with different user accounts - # We note that this portal could be specific to a given registry, to which we register users, but i'm not sure that simplifies things - # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn - - #messages.info(self.request, 'You have logged in') - page = Page(self.request) - - ctx_my_authorities = {} - ctx_delegation_authorities = {} - - - # The user need to be logged in - if the_user(self.request): - # Who can a PI validate: - # His own authorities + those he has credentials for. - # In MySlice we need to look at credentials also. - - - # XXX This will have to be asynchroneous. Need to implement barriers, - # for now it will be sufficient to have it working statically - - # get user_id to later on query accounts - # XXX Having real query plan on local tables would simplify all this - # XXX $user_email is still not available for local tables - #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id') - user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id') - user, = execute_query(self.request, user_query) - user_id = user['user_id'] - - # Query manifold to learn about available SFA platforms for more information - # In general we will at least have the portal - # For now we are considering all registries - all_authorities = [] - platform_ids = [] - sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type') - sfa_platforms = execute_query(self.request, sfa_platforms_query) - for sfa_platform in sfa_platforms: - print "SFA PLATFORM > ", sfa_platform['platform'] - if not 'auth_type' in sfa_platform: - continue - auth = sfa_platform['auth_type'] - if not auth in all_authorities: - all_authorities.append(auth) - platform_ids.append(sfa_platform['platform_id']) - - print "W: Hardcoding platform myslice" - # There has been a tweak on how new platforms are referencing a - # so-called 'myslice' platform for storing authentication tokens. - # XXX This has to be removed in final versions. - myslice_platforms_query = Query().get('local:platform').filter_by('platform', '==', 'myslice').select('platform_id') - myslice_platforms = execute_query(self.request, myslice_platforms_query) - if myslice_platforms: - myslice_platform, = myslice_platforms - platform_ids.append(myslice_platform['platform_id']) - - # We can check on which the user has authoritity credentials = PI rights - credential_authorities = set() - credential_authorities_expired = set() - - # User account on these registries - user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('auth_type', 'config') - user_accounts = execute_query(self.request, user_accounts_query) - #print "=" * 80 - #print user_accounts - #print "=" * 80 - for user_account in user_accounts: - - print "USER ACCOUNT", user_account - if user_account['auth_type'] == 'reference': - continue # we hardcoded the myslice platform... - - config = json.loads(user_account['config']) - creds = [] - print "CONFIG KEYS", config.keys() - if 'authority_credentials' in config: - print "***", config['authority_credentials'].keys() - for authority_hrn, credential in config['authority_credentials'].items(): - #if credential is not expired: - credential_authorities.add(authority_hrn) - #else - # credential_authorities_expired.add(authority_hrn) - if 'delegated_authority_credentials' in config: - print "***", config['delegated_authority_credentials'].keys() - for authority_hrn, credential in config['delegated_authority_credentials'].items(): - #if credential is not expired: - credential_authorities.add(authority_hrn) - #else - # credential_authorities_expired.add(authority_hrn) - - print 'credential_authorities =', credential_authorities - print 'credential_authorities_expired =', credential_authorities_expired - - # ** Where am I a PI ** - # For this we need to ask SFA (of all authorities) = PI function - pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') - pi_authorities_tmp = execute_query(self.request, pi_authorities_query) - pi_authorities = set() - for pa in pi_authorities_tmp: - pi_authorities |= set(pa['pi_authorities']) - - print "pi_authorities =", pi_authorities - - # My authorities + I have a credential - pi_credential_authorities = pi_authorities & credential_authorities - pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired - pi_expired_credential_authorities = pi_authorities & credential_authorities_expired - # Authorities I've been delegated PI rights - pi_delegation_credential_authorities = credential_authorities - pi_authorities - pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities - - print "pi_credential_authorities =", pi_credential_authorities - print "pi_no_credential_authorities =", pi_no_credential_authorities - print "pi_expired_credential_authorities =", pi_expired_credential_authorities - print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities - print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities - - # Summary intermediary - pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities - pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities - - print "--" - print "pi_my_authorities = ", pi_my_authorities - print "pi_delegation_authorities = ", pi_delegation_authorities - - # Summary all - queried_pending_authorities = pi_my_authorities | pi_delegation_authorities - print "----" - print "queried_pending_authorities = ", queried_pending_authorities - - requests = get_request_by_authority(queried_pending_authorities) - for request in requests: - auth_hrn = request['authority_hrn'] - - if auth_hrn in pi_my_authorities: - dest = ctx_my_authorities - - # define the css class - if auth_hrn in pi_credential_authorities: - request['allowed'] = 'allowed' - elif auth_hrn in pi_expired_credential_authorities: - request['allowed'] = 'expired' - else: # pi_no_credential_authorities - request['allowed'] = 'denied' - - elif auth_hrn in pi_delegation_authorities: - dest = ctx_delegation_authorities - - if auth_hrn in pi_delegation_credential_authorities: - request['allowed'] = 'allowed' - else: # pi_delegation_expired_authorities - request['allowed'] = 'expired' - - else: - continue - - if not auth_hrn in dest: - dest[auth_hrn] = [] - dest[auth_hrn].append(request) - - context = super(ValidatePendingView, self).get_context_data(**kwargs) - context['my_authorities'] = ctx_my_authorities - context['delegation_authorities'] = ctx_delegation_authorities - - # XXX This is repeated in all pages - # more general variables expected in the template - context['title'] = 'Test view that combines various plugins' - # the menu items on the top - context['topmenu_items'] = topmenu_items_live('Validation', page) - # so we can sho who is logged - context['username'] = the_user(self.request) - - # XXX We need to prepare the page for queries - #context.update(page.prelude_env()) - - return context diff --git a/ui/topmenu.py b/ui/topmenu.py index be21db22..b0ff707f 100644 --- a/ui/topmenu.py +++ b/ui/topmenu.py @@ -39,6 +39,7 @@ def topmenu_items_static (current, request): # looks like this is accessible to non-logged users result.append({'label':'Platforms', 'href': '/portal/platforms/'}) result.append({'label':'Register', 'href': '/portal/register/'}) + result.append({'label':'Join us', 'href': '/portal/join/'}) result.append({'label':'Contact Support', 'href': '/portal/contact/'}) # mark active if the provided 'current', even if shorter, matches the beginning of d['label'] @@ -69,7 +70,7 @@ from plugins.topmenuvalidation import TopmenuValidation # for asynchronous management of topmenu def topmenu_items_live (current, page): request=page.request - query_pi_auths = Query.get('ple:user').filter_by('user_hrn', '==', '$user_hrn' ).select('pi_authorities') + query_pi_auths = Query.get('user').filter_by('user_hrn', '==', '$user_hrn' ).select('pi_authorities') page.enqueue_query(query_pi_auths) # # even though this plugin does not have any html materialization, the corresponding domid # # must exist because it is searched at init-time to create the JS plugin