def login_begin(request, attribute_set='default', form_class=OpenIDLoginForm): """Begin an OpenID login request, possibly asking for an identity URL.""" redirect_to = OpenIDBackend.get_redirect_to(request) openid_url = getattr(settings, 'MOJEID_ENDPOINT_URL', MOJEID_ENDPOINT_URL) login_form = form_class(data=request.POST) if login_form.is_valid(): openid_url = login_form.cleaned_data['openid_identifier'] consumer = make_consumer(request) # Set response handler (define the settings set) consumer.session['attribute_set'] = attribute_set # Set the language consumer.session['stored_lang'] = request.POST.get('lang', get_language()) request.session.save() try: openid_request = consumer.begin(openid_url) except DiscoveryFailure as exc: return render_failure(request, errors.DiscoveryError(exc)) # Request user details. attributes = get_attribute_query(attribute_set) fetch_request = ax.FetchRequest() for attribute, required in attributes: fetch_request.add(attribute.generate_ax_attrinfo(required)) if attributes: openid_request.addExtension(fetch_request) if getattr(settings, 'OPENID_PHYSICAL_MULTIFACTOR_REQUIRED', False): preferred_auth = [ pape.AUTH_MULTI_FACTOR_PHYSICAL, ] pape_request = pape.Request(preferred_auth_policies=preferred_auth) openid_request.addExtension(pape_request) # Construct the request completion URL, including the page we # should redirect to. return_to = request.build_absolute_uri(reverse(login_complete)) if redirect_to: if '?' in return_to: return_to += '&' else: return_to += '?' # Django gives us Unicode, which is great. We must encode URI. # urllib enforces str. We can't trust anything about the default # encoding inside str(foo) , so we must explicitly make foo a str. return_to += urlencode( {OpenIDBackend.get_redirect_field_name(): redirect_to.encode("UTF-8")}) return render_openid_request(request, openid_request, return_to)
def authenticate_user(**kwargs): """ Display user forms prefilled with data from mojeID """ request = kwargs['request'] openid_response = kwargs['openid_response'] redirect_to = kwargs['redirect'] user_model = get_user_model() # Get the user try: # Authenticate user user_openid = UserOpenID.objects.get( claimed_id__exact=openid_response.identity_url) try: user = user_model.objects.get(pk=user_openid.user_id) if OpenIDBackend.is_user_authenticated(user): OpenIDBackend.associate_user_with_session(request, user) except user_model.DoesNotExist: pass # Update all updatable attributes #attrs = OpenIDBackend.update_user_from_openid(user_id, openid_response) # Or Just display the updatable attributes to be updated attrs = OpenIDBackend.get_model_changes(openid_response, only_updatable=True) # Set url path path = reverse(display_user) except UserOpenID.DoesNotExist: # Create user # Get attributes for the new User model attrs = OpenIDBackend.get_model_changes(openid_response) # Set url path path = reverse(new_user) # set the params for redirect qd = QueryDict('').copy() params = attrs.get(get_user_model(), {}) params['next'] = redirect_to if 'user_id_field_name' in params: del params['user_id_field_name'] qd.update(params) # TODO claimed_id as a param! url = "%s?%s" % (path, qd.urlencode()) return redirect(url)
def login(request): # Get the redirect field from url redirect = OpenIDBackend.get_redirect_to(request) # If the redirect is not in url get the defualt redirect = redirect if redirect else getattr(settings, "LOGIN_REDIRECT_URL", None) return render(request, 'login.html', {'redirect': redirect})
def login_begin(request, attribute_set='default'): """Begin an MojeID login request.""" if mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR in request.session: del request.session[mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR] # create consumer, start login process consumer = MojeIDConsumer(DjangoOpenIDStore()) openid_request = consumer.begin(create_service()) # Request user details. attributes = get_attribute_query(attribute_set) # save settings set name for response handler request.session[SESSION_ATTR_SET_KEY] = attribute_set fetch_request = ax.FetchRequest() for attribute, required in attributes: fetch_request.add(attribute.generate_ax_attrinfo(required)) if attributes: openid_request.addExtension(fetch_request) if mojeid_settings.MOJEID_LOGIN_METHOD != 'ANY' or \ mojeid_settings.MOJEID_MAX_AUTH_AGE is not None: # set authentication method to OTP or CERT if mojeid_settings.MOJEID_LOGIN_METHOD == "OTP": auth_method = [pape.AUTH_MULTI_FACTOR] elif mojeid_settings.MOJEID_LOGIN_METHOD == "CERT": auth_method = [pape.AUTH_PHISHING_RESISTANT] else: auth_method = None pape_request = pape.Request( preferred_auth_policies=auth_method, max_auth_age=mojeid_settings.MOJEID_MAX_AUTH_AGE, ) openid_request.addExtension(pape_request) # Construct the request completion URL return_to = request.build_absolute_uri(reverse(login_complete)) # get 'next page' and save it to the session redirect_to = sanitise_redirect_url(OpenIDBackend.get_redirect_to(request)) if redirect_to: request.session[mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR] = redirect_to # Realm should be always something like 'https://example.org/openid/' realm = getattr(settings, 'MOJEID_REALM', None) if not realm: realm = request.build_absolute_uri(reverse(top)) # we always use POST request form_html = openid_request.htmlMarkup( realm, return_to, form_tag_attrs={'id': 'openid_message'}) return HttpResponse(form_html, content_type='text/html; charset=UTF-8')
def disassociate(request): """ Disassociate current user with OpenID """ # Get the User user = OpenIDBackend.get_user_from_request(request) if not user: raise Http404 # Get OpenID association association = OpenIDBackend.get_user_association(user) if not association: raise Http404 # Remove the association association.delete() # Redirect back redirect = OpenIDBackend.get_redirect_to(request) redirect = redirect if redirect else getattr(settings, 'LOGIN_REDIRECT_URL', '/') return HttpResponseRedirect(sanitise_redirect_url(redirect))
def index(request): extra = None user = None if request.user.is_authenticated(): user = request.user if request.user.userextraattributes_set.exists(): extra = request.user.userextraattributes_set.all()[0] return render( request, 'index.html', { 'user': user, 'association': OpenIDBackend.get_user_association(user), 'extra': extra, 'attribute_set': 'default', })
def login_show(request, login_template='openid/login.html', associate_template='openid/associate.html', form_class=OpenIDLoginForm): """ Render a template to show the login/associate form form. """ redirect_to = OpenIDBackend.get_redirect_to(request) login_form = form_class(request.POST or None) user = OpenIDBackend.get_user_from_request(request) template_name = associate_template if user else login_template return render_to_response( template_name, { 'form': login_form, 'action': reverse('openid-init'), OpenIDBackend.get_redirect_field_name(): redirect_to }, context_instance=RequestContext(request) )
def index(request): extra = None user = None if request.user.is_authenticated(): user = request.user if request.user.userextraattributes_set.exists(): extra = request.user.userextraattributes_set.all()[0] return render(request, 'index.html', { 'user': user, 'association': OpenIDBackend.get_user_association(user), 'extra': extra, 'attribute_set': 'default', } )
def associate_user(**kwargs): """ Display associate forms prefilled with data from mojeID """ request = kwargs['request'] openid_response = kwargs['openid_response'] redirect_to = kwargs['redirect'] claimed_id = openid_response.endpoint.claimed_id user_associated = None claimed_associated = None # Check whether the claimed_id is already associated if UserOpenID.objects.filter(claimed_id__exact=claimed_id).exists(): path = reverse(display_disassociate) user_associated = User.objects.get(pk=UserOpenID.objects.get( claimed_id__exact=claimed_id).user_id) else: if UserOpenID.objects.filter(user_id=request.user.id).exists(): path = reverse(display_disassociate) claimed_associated = UserOpenID.objects.get( user_id=request.user.id).claimed_id else: path = reverse(display_associate) # Associate the user #OpenIDBackend.associate_openid_response(request.user, openid_response) # Update all updatable attributes #attrs = OpenIDBackend.update_user_from_openid(user_id, openid_response) # Or Just display the updatable attributes to be updated attrs = OpenIDBackend.get_model_changes(openid_response, only_updatable=True) # set the params for redirect qd = QueryDict('').copy() params = attrs.get(User, {}) params['next'] = redirect_to params['claimed_id'] = claimed_id if user_associated: params['user_associated'] = user_associated if claimed_associated: params['claimed_associated'] = claimed_associated if 'user_id_field_name' in params: del params['user_id_field_name'] qd.update(params) url = "%s?%s" % (path, qd.urlencode()) return redirect(url)
def associate_user(**kwargs): """ Display associate forms prefilled with data from mojeID """ request = kwargs['request'] openid_response = kwargs['openid_response'] redirect_to = kwargs['redirect'] claimed_id = openid_response.endpoint.claimed_id user_associated = None claimed_associated = None # Check whether the claimed_id is already associated if UserOpenID.objects.filter(claimed_id__exact=claimed_id).exists(): path = reverse(display_disassociate) user_associated = User.objects.get(pk=UserOpenID.objects.get( claimed_id__exact=claimed_id).user_id) else: if UserOpenID.objects.filter(user_id=request.user.id).exists(): path = reverse(display_disassociate) claimed_associated = UserOpenID.objects.get(user_id=request.user.id).claimed_id else: path = reverse(display_associate) # Associate the user #OpenIDBackend.associate_openid_response(request.user, openid_response) # Update all updatable attributes #attrs = OpenIDBackend.update_user_from_openid(user_id, openid_response) # Or Just display the updatable attributes to be updated attrs = OpenIDBackend.get_model_changes(openid_response, only_updatable=True) # set the params for redirect qd = QueryDict('').copy() params = attrs.get(User, {}) params['next'] = redirect_to params['claimed_id'] = claimed_id if user_associated: params['user_associated'] = user_associated if claimed_associated: params['claimed_associated'] = claimed_associated if 'user_id_field_name' in params: del params['user_id_field_name'] qd.update(params) url = "%s?%s" % (path, qd.urlencode()) return redirect(url)
def registration(request, attribute_set='default', template_name='openid/registration_form.html', form_class=OpenIDLoginForm): """ Try to submit all the registration attributes for mojeID registration""" registration_url = getattr(settings, 'MOJEID_REGISTRATION_URL', MOJEID_REGISTRATION_URL) # Realm should be always something like 'https://example.org/openid/' realm = getattr(settings, 'MOJEID_REALM', request.build_absolute_uri(reverse(top))) user = OpenIDBackend.get_user_from_request(request) user_id = user.pk if user else None # Create Nonce nonce = Nonce(server_url=realm, user_id=user_id) nonce.save() fields = [] attributes = [x for x in get_attributes(attribute_set) if x.type == 'attribute'] # Append attributes to creation request if user is valid if user: for attribute in attributes: form_attr = attribute.registration_form_attrs_html(user_id) if form_attr: fields.append(form_attr) # Render the redirection template return render_to_response( template_name, { 'fields': fields, 'action': registration_url, 'realm': realm, 'nonce': nonce.registration_nonce, }, context_instance=RequestContext(request) )
def registration(request, attribute_set='default', template_name='openid/registration_form.html'): """ Try to submit all the registration attributes for mojeID registration""" # Realm should be always something like 'https://example.org/openid/' realm = getattr(settings, 'MOJEID_REALM', request.build_absolute_uri(reverse(top))) user = OpenIDBackend.get_user_from_request(request) user_id = user.pk if user else None # Create Nonce nonce = Nonce(server_url=realm, user_id=user_id, timestamp=time.time(), salt=randomString(35, NONCE_CHARS)) nonce.save() fields = [] attributes = [x for x in get_attributes(attribute_set) if x.type == 'attribute'] # Append attributes to creation request if user is valid if user: for attribute in attributes: form_attr = attribute.registration_form_attrs_html(user_id) if form_attr: fields.append(form_attr) # Render the redirection template return render_to_response( template_name, { 'fields': fields, 'action': get_registration_url(), 'realm': realm, 'nonce': nonce.registration_nonce, }, context_instance=RequestContext(request) )
def assertion(request): """ mojeID server connects here to propagate a response to the registration """ def _reject(request, error): """ Reject response """ return HttpResponse(dictToKV({'mode': 'reject', 'reason': error})) def _accept(request): """ Accept response """ return HttpResponse(dictToKV({'mode': 'accept'})) # Accept only post if not request.method == 'POST': return _reject(request, Assertion.ErrorString.BAD_REQUEST) # Accept only valid status status = request.POST.get('status', None) if not status: return _reject(request, Assertion.ErrorString.MISSING_STATUS) if not status in Assertion.StatusCodes: return _reject(request, Assertion.ErrorString.INVALID_STATUS) # TODO check whether this request is from mojeID server and uses https with a proper certificate # Test calimed ID claimed_id = request.POST.get('claimed_id') if not claimed_id: return _reject(request, Assertion.ErrorString.MISSING_CLAIMED_ID) # The user was registered for mojeID if status == Assertion.StatusCodes.REGISTERED: registration_nonce = request.POST.get('registration_nonce') if registration_nonce is None: return _reject(request, Assertion.ErrorString.MISSING_NONCE) # check nonce try: nonce = Nonce.get_registration_nonce(registration_nonce) except Nonce.DoesNotExist: return _reject(request, Assertion.ErrorString.INVALID_NONCE) user_id = nonce.user_id nonce.delete() # Fetch the user user_model = get_user_model() try: user = user_model.objects.get(pk=user_id) # Create association OpenIDBackend.associate_openid(user, claimed_id) except (user_model.DoesNotExist, IdentityAlreadyClaimed): # Don't associte the user when the user doesn't exist or is already claimed # And assume that server sent us a valid claimed_id # # Note that user might been deleted before this assertion is triggered # Or the newly created mojeID account might been already associated # with a local account by the client # # Both of these cases are not considered as errors pass return _accept(request)
def login_complete(request): # Get addres where to redirect after the login redirect_to = sanitise_redirect_url(OpenIDBackend.get_redirect_to(request)) # Get OpenID response and test whether it is valid attribute_set, lang, openid_response = parse_openid_response(request) # Set language activate_lang(lang) if not openid_response: return render_failure(request, errors.EndpointError()) # Check whether the user is already logged in user_orig = OpenIDBackend.get_user_from_request(request) user_model = get_user_model() if openid_response.status == SUCCESS: try: if user_orig: # Send a signal to obtain HttpResponse resp = associate_user.send(sender=__name__, request=request, openid_response=openid_response, attribute_set=attribute_set, redirect=redirect_to) resp = [r[1] for r in resp if isinstance(r[1], HttpResponse)] if resp: # Return first valid response return resp[0] # Create association with currently logged in user OpenIDBackend.associate_openid_response(user_orig, openid_response) else: # Authenticate mojeID user. # Send a signal to obtain HttpResponse resp = authenticate_user.send(sender=__name__, request=request, openid_response=openid_response, attribute_set=attribute_set, redirect=redirect_to) resp = [r[1] for r in resp if isinstance(r[1], HttpResponse)] if resp: # Return first valid response return resp[0] # Perform a default action user_new = OpenIDBackend.authenticate_using_all_backends( openid_response=openid_response, attribute_set=attribute_set) if not user_new: # Failed to create a user return render_failure(request, errors.UnknownUser()) if not OpenIDBackend.is_user_active(user_new): # user is deactivated return render_failure(request, errors.DisabledAccount(user_new)) # Create an association with the new user OpenIDBackend.associate_user_with_session(request, user_new) except DjangoOpenIDException as e: # Something went wrong user_id = None try: # Try to get user id user_id = UserOpenID.objects.get(claimed_id=openid_response.identity_url).user_id except (UserOpenID.DoesNotExist, user_model.DoesNotExist): # Report an error with identity_url user_login_report.send(sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False) # Report an error with the username user_login_report.send(sender=__name__, request=request, username=openid_response.identity_url, user_id=user_id, method='openid', success=False) # Render the failure page return render_failure(request, errors.AuthenticationFailed(e)) response = HttpResponseRedirect(redirect_to) # Send signal to log the successful login attempt user_login_report.send(sender=__name__, request=request, user_id=user_orig.id if user_orig else user_new.id, method='openid', success=True) return response # Render other failures elif openid_response.status == FAILURE: user_login_report.send(sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False) return render_failure(request, errors.OpenIDAuthenticationFailed(openid_response)) elif openid_response.status == CANCEL: user_login_report.send(sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False) return render_failure(request, errors.OpenIDAuthenticationCanceled()) else: user_login_report.send(sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False) return render_failure(request, errors.OpenIDUnknownResponseType(openid_response))
def login_complete(request): # Get addres where to redirect after the login redirect_to = sanitise_redirect_url( request.session.get(mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR)) attribute_set = request.session.get(SESSION_ATTR_SET_KEY, 'default') # clean the session if mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR in request.session: del request.session[mojeid_settings.MOJEID_SESSION_NEXT_PAGE_ATTR] if SESSION_ATTR_SET_KEY in request.session: del request.session[SESSION_ATTR_SET_KEY] # Get OpenID response and test whether it is valid endpoint = create_service() message = Message.fromPostArgs(request.REQUEST) consumer = MojeIDConsumer(DjangoOpenIDStore()) try: openid_response = consumer.complete( message, endpoint, request.build_absolute_uri()) except HTTPFetchingError: # if not using association and can't contact MojeID server return render_failure(request, errors.EndpointError()) # Check whether the user is already logged in user_orig = OpenIDBackend.get_user_from_request(request) user_model = get_user_model() if openid_response.status == SUCCESS: try: if user_orig: # Send a signal to obtain HttpResponse resp = associate_user.send( sender=__name__, request=request, openid_response=openid_response, attribute_set=attribute_set, redirect=redirect_to ) resp = [r[1] for r in resp if isinstance(r[1], HttpResponse)] if resp: # Return first valid response return resp[0] # Create association with currently logged in user OpenIDBackend.associate_openid_response(user_orig, openid_response) else: # Authenticate mojeID user. # Send a signal to obtain HttpResponse resp = authenticate_user.send( sender=__name__, request=request, openid_response=openid_response, attribute_set=attribute_set, redirect=redirect_to ) resp = [r[1] for r in resp if isinstance(r[1], HttpResponse)] if resp: # Return first valid response return resp[0] # Perform a default action user_new = OpenIDBackend.authenticate_using_all_backends( openid_response=openid_response, attribute_set=attribute_set) if not user_new: # Failed to create a user return render_failure(request, errors.UnknownUser()) if not OpenIDBackend.is_user_active(user_new): # user is deactivated return render_failure(request, errors.DisabledAccount(user_new)) # Create an association with the new user OpenIDBackend.associate_user_with_session(request, user_new) except DjangoOpenIDException as e: # Something went wrong user_id = None try: # Try to get user id user_id = UserOpenID.objects.get(claimed_id=openid_response.identity_url).user_id except (UserOpenID.DoesNotExist, user_model.DoesNotExist): # Report an error with identity_url user_login_report.send( sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False ) # Report an error with the username user_login_report.send( sender=__name__, request=request, username=openid_response.identity_url, user_id=user_id, method='openid', success=False ) # Render the failure page return render_failure(request, errors.AuthenticationFailed(e)) response = HttpResponseRedirect(redirect_to) # Send signal to log the successful login attempt user_login_report.send( sender=__name__, request=request, user_id=user_orig.id if user_orig else user_new.id, method='openid', success=True ) return response # Render other failures elif openid_response.status == FAILURE: user_login_report.send( sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False ) return render_failure(request, errors.OpenIDAuthenticationFailed(openid_response)) elif openid_response.status == CANCEL: user_login_report.send( sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False ) return render_failure(request, errors.OpenIDAuthenticationCanceled()) else: user_login_report.send( sender=__name__, request=request, username=openid_response.identity_url, method='openid', success=False ) return render_failure(request, errors.OpenIDUnknownResponseType(openid_response))
def assertion(request): """ mojeID server connects here to propagate a response to the registration """ def _reject(request, error): """ Reject response """ return HttpResponse(dictToKV({'mode': 'reject', 'reason': error})) def _accept(request): """ Accept response """ return HttpResponse(dictToKV({'mode': 'accept'})) # Accept only post if not request.method == 'POST': return _reject(request, Assertion.ErrorString.BAD_REQUEST) # Accept only valid status status = request.POST.get('status', None) if not status: return _reject(request, Assertion.ErrorString.MISSING_STATUS) if status not in Assertion.StatusCodes: return _reject(request, Assertion.ErrorString.INVALID_STATUS) # TODO check whether this request is from mojeID server and uses https with a proper certificate # Test calimed ID claimed_id = request.POST.get('claimed_id') if not claimed_id: return _reject(request, Assertion.ErrorString.MISSING_CLAIMED_ID) # The user was registered for mojeID if status == Assertion.StatusCodes.REGISTERED: registration_nonce = request.POST.get('registration_nonce') if registration_nonce is None: return _reject(request, Assertion.ErrorString.MISSING_NONCE) # check nonce try: nonce = Nonce.get_registration_nonce(registration_nonce) except Nonce.DoesNotExist: return _reject(request, Assertion.ErrorString.INVALID_NONCE) user_id = nonce.user_id nonce.delete() # Try to associate the user with mojeID if user_id: # Fetch the user user_model = get_user_model() try: user = user_model.objects.get(pk=user_id) # Create association OpenIDBackend.associate_openid(user, claimed_id) except (user_model.DoesNotExist, IdentityAlreadyClaimed): # Don't associte the user when the user doesn't exist or is already claimed # And assume that server sent us a valid claimed_id # # Note that user might been deleted before this assertion is triggered # Or the newly created mojeID account might been already associated # with a local account by the client # # Both of these cases are not considered as errors pass return _accept(request)