def test_login_require(self): from authentic2.utils import login_require from django.test.client import RequestFactory rf = RequestFactory() request = rf.get('/coin', data={'next': '/zob/', 'nonce': 'xxx'}) response = login_require(request) self.assertEqualsURL(response['Location'].split('?', 1)[0], '/login/') self.assertEqualsURL( urlparse.parse_qs( response['Location'].split('?', 1)[1])['next'][0], '/coin?nonce=xxx&next=/zob/')
def authenticate(self, request, st): ''' Redirect to an login page, pass a cookie to the login page to associate the login event with the service ticket, if renew was asked ''' nonce = st.ticket_id next_url = make_url('a2-idp-cas-continue', params={ SERVICE_PARAM: st.service_url, NONCE_FIELD_NAME: nonce}) return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce})
def authenticate(self, request, st): ''' Redirect to an login page, pass a cookie to the login page to associate the login event with the service ticket, if renew was asked ''' nonce = st.ticket_id next_url = make_url('a2-idp-cas-continue', params={ SERVICE_PARAM: st.service_url, NONCE_FIELD_NAME: nonce }) return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce})
def authorize(request, *args, **kwargs): logger = logging.getLogger(__name__) start = now() try: client_id = request.GET['client_id'] redirect_uri = request.GET['redirect_uri'] except KeyError as k: return HttpResponseBadRequest('invalid request: missing parameter %s' % k.args[0], content_type='text/plain') try: client = models.OIDCClient.objects.get(client_id=client_id) except models.OIDCClient.DoesNotExist: return HttpResponseBadRequest('invalid request: unknown client_id', content_type='text/plain') fragment = client.authorization_flow == client.FLOW_IMPLICIT state = request.GET.get('state') try: response_type = request.GET['response_type'] scope = request.GET['scope'] except KeyError as k: return authorization_error(request, redirect_uri, 'invalid_request', state=state, error_description='missing parameter %s' % k.args[0], fragment=fragment) prompt = set(filter(None, request.GET.get('prompt', '').split())) nonce = request.GET.get('nonce') scopes = utils.scope_set(scope) max_age = request.GET.get('max_age') if max_age: try: max_age = int(max_age) if max_age < 0: raise ValueError except ValueError: return authorization_error(request, redirect_uri, 'invalid_request', error_description='max_age is not a positive integer', state=state, fragment=fragment) if redirect_uri not in client.redirect_uris.split(): return authorization_error(request, redirect_uri, 'invalid_request', error_description='unauthorized redirect_uri', state=state, fragment=fragment) if client.authorization_flow == client.FLOW_AUTHORIZATION_CODE: if response_type != 'code': return authorization_error(request, redirect_uri, 'unsupported_response_type', error_description='only code is supported', state=state, fragment=fragment) elif client.authorization_flow == client.FLOW_IMPLICIT: if not set(filter(None, response_type.split())) in (set(['id_token', 'token']), set(['id_token'])): return authorization_error(request, redirect_uri, 'unsupported_response_type', error_description='only "id_token token" or "id_token" ' 'are supported', state=state, fragment=fragment) else: raise NotImplementedError if 'openid' not in scopes: return authorization_error(request, redirect_uri, 'invalid_request', error_description='openid scope is missing', state=state, fragment=fragment) allowed_scopes = app_settings.SCOPES or ['openid', 'email', 'profile'] if not (scopes <= set(allowed_scopes)): message = 'only "%s" scope(s) are supported, but "%s" requested' % ( ', '.join(allowed_scopes), ', '.join(scopes)) return authorization_error(request, redirect_uri, 'invalid_scope', error_description=message, state=state, fragment=fragment) hooks.call_hooks('event', name='sso-request', idp='oidc', service=client) # authentication canceled by user if 'cancel' in request.GET: logger.info(u'authentication canceled for service %s', client.name) return authorization_error(request, redirect_uri, 'access_denied', error_description='user did not authenticate', state=state, fragment=fragment) if not request.user.is_authenticated() or 'login' in prompt: if 'none' in prompt: return authorization_error(request, redirect_uri, 'login_required', error_description='login is required but prompt is none', state=state, fragment=fragment) params = {} if nonce is not None: params['nonce'] = nonce return login_require(request, params=params, service=client) # if user not authorized, a ServiceAccessDenied exception # is raised and handled by ServiceAccessMiddleware client.authorize(request.user) last_auth = last_authentication_event(request.session) if max_age is not None and time.time() - last_auth['when'] >= max_age: if 'none' in prompt: return authorization_error(request, redirect_uri, 'login_required', error_description='login is required but prompt is none', state=state, fragment=fragment) params = {} if nonce is not None: params['nonce'] = nonce return login_require(request, params=params, service=client) if client.authorization_mode != client.AUTHORIZATION_MODE_NONE or 'consent' in prompt: # authorization by user is mandatory, as per local configuration or per explicit request by # the RP if client.authorization_mode in (client.AUTHORIZATION_MODE_NONE, client.AUTHORIZATION_MODE_BY_SERVICE): auth_manager = client.authorizations elif client.authorization_mode == client.AUTHORIZATION_MODE_BY_OU: auth_manager = client.ou.oidc_authorizations qs = auth_manager.filter(user=request.user) if 'consent' in prompt: # if consent is asked we delete existing authorizations # it seems to be the safer option qs.delete() qs = auth_manager.none() else: qs = qs.filter(expired__gte=start) authorized_scopes = set() for authorization in qs: authorized_scopes |= authorization.scope_set() if (authorized_scopes & scopes) < scopes: if 'none' in prompt: return authorization_error( request, redirect_uri, 'consent_required', error_description='consent is required but prompt is none', state=state, fragment=fragment) if request.method == 'POST': if 'accept' in request.POST: pk_to_deletes = [] for authorization in qs: # clean obsolete authorizations if authorization.scope_set() <= scopes: pk_to_deletes.append(authorization.pk) auth_manager.create( user=request.user, scopes=u' '.join(sorted(scopes)), expired=start + datetime.timedelta(days=365)) if pk_to_deletes: auth_manager.filter(pk__in=pk_to_deletes).delete() logger.info(u'authorized scopes %s for service %s', ' '.join(scopes), client.name) else: logger.info(u'refused scopes %s for service %s', ' '.join(scopes), client.name) return authorization_error(request, redirect_uri, 'access_denied', error_description='user denied access', state=state, fragment=fragment) else: return render(request, 'authentic2_idp_oidc/authorization.html', { 'client': client, 'scopes': scopes - set(['openid']), }) if response_type == 'code': code = models.OIDCCode.objects.create( client=client, user=request.user, scopes=u' '.join(scopes), state=state, nonce=nonce, redirect_uri=redirect_uri, expired=start + datetime.timedelta(seconds=30), auth_time=datetime.datetime.fromtimestamp(last_auth['when'], utc), session_key=request.session.session_key) logger.info(u'sending code %s for scopes %s for service %s', code.uuid, ' '.join(scopes), client.name) params = { 'code': unicode(code.uuid), } if state is not None: params['state'] = state response = redirect(request, redirect_uri, params=params, resolve=False) else: # FIXME: we should probably factorize this part with the token endpoint similar code need_access_token = 'token' in response_type.split() expires_in = 3600 * 8 if need_access_token: access_token = models.OIDCAccessToken.objects.create( client=client, user=request.user, scopes=u' '.join(scopes), session_key=request.session.session_key, expired=start + datetime.timedelta(seconds=expires_in)) acr = '0' if nonce is not None and last_auth.get('nonce') == nonce: acr = '1' id_token = utils.create_user_info(client, request.user, scopes, id_token=True) id_token.update({ 'iss': utils.get_issuer(request), 'aud': client.client_id, 'exp': timestamp_from_datetime(start + idtoken_duration(client)), 'iat': timestamp_from_datetime(start), 'auth_time': last_auth['when'], 'acr': acr, 'sid': utils.get_session_id(request, client), }) if nonce is not None: id_token['nonce'] = nonce params = { 'id_token': utils.make_idtoken(client, id_token), } if state is not None: params['state'] = state if need_access_token: params.update({ 'access_token': access_token.uuid, 'token_type': 'Bearer', 'expires_in': expires_in, }) # query is transfered through the hashtag response = redirect(request, redirect_uri + '#%s' % urlencode(params), resolve=False) hooks.call_hooks('event', name='sso-success', idp='oidc', service=client, user=request.user) utils.add_oidc_session(request, client) return response
def openid_decide(request): """ The page that asks the user if they really want to sign in to the site, and lets them add the consumer to their trusted whitelist. # If user is logged in, ask if they want to trust this trust_root # If they are NOT logged in, show the landing page """ orequest = request.session.get("OPENID_REQUEST") # No request ? Failure.. if not orequest: logger.warning( "OpenID decide view failed, \ because no OpenID request is saved" ) return redirect("auth_homepage") sreg_request = SRegRequest.fromOpenIDRequest(orequest) logger.debug("SREG request: %s" % sreg_request.__dict__) if not request.user.is_authenticated(): # Not authenticated ? Authenticate and go back to the server endpoint return login_require(request, params={NONCE_FIELD_NAME: "1"}) if request.method == "POST": if "cancel" in request.POST: # User refused logger.info("OpenID decide canceled") return redirect(openid_server, params={"cancel": ""}) else: form = DecideForm(sreg_request=sreg_request, data=request.POST) if form.is_valid(): data = form.cleaned_data # Remember the choice t, created = models.TrustedRoot.objects.get_or_create( user=request.user.id, trust_root=orequest.trust_root ) t.choices = sreg_request.required + [field for field in data if data[field]] t.save() logger.debug("OpenID decide, user choice:%s" % data) return redirect("openid-provider-root") else: form = DecideForm(sreg_request=sreg_request) logger.info("OpenID device view, orequest:%s" % orequest) # verify return_to of trust_root try: trust_root_valid = verifyReturnTo(orequest.trust_root, orequest.return_to) and "Valid" or "Invalid" except HTTPFetchingError: trust_root_valid = "Unreachable" except DiscoveryFailure: trust_root_valid = "DISCOVERY_FAILED" return render_to_response( "idp/openid/decide.html", { "title": _("Trust this site?"), "required": sreg_request.required, "optional": sreg_request.optional, "trust_root_valid": trust_root_valid, "form": form, }, context_instance=RequestContext(request), )
if not request.user.is_authenticated(): # Site does not want interaction if orequest.immediate: logger.debug( "User not logged and checkid immediate request, \ returning OpenID failure" ) return oresponse_to_response(server, orequest.answer(False)) else: # Try to login request.session["OPENID_REQUEST"] = orequest logger.debug( "User not logged and checkid request, \ redirecting to login page" ) return login_require(request, params={NONCE_FIELD_NAME: "1"}) else: identity = orequest.identity if identity != IDENTIFIER_SELECT: exploded = urlparse.urlparse(identity) # Allows only /openid/<user_id> if check_exploded(exploded, request): # We only support directed identity logger.debug("Invalid OpenID identity %s" % identity) return oresponse_to_response(server, orequest.answer(False)) if getattr(settings, "RESTRICT_OPENID_RP", None): logger.debug("RP restriction is activated") if orequest.trust_root in getattr(settings, "RESTRICT_OPENID_RP"): logger.debug("The RP %s is authorized" % orequest.trust_root) else: logger.debug("The RP %s is not authorized, return 404." % orequest.trust_root)