Example #1
0
 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/')
Example #2
0
 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})
Example #3
0
 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})
Example #4
0
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
Example #5
0
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),
    )
Example #6
0
        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)