Example #1
0
def openid_server(req):
    """
    This view is the actual OpenID server - running at the URL pointed to by 
    the <link rel="openid.server"> tag. 
    """
    host = get_base_uri(req)
    try:
        # if we have django_openid_auth in applications directory
        # then we can use DjangoOpenIDStore
        from django_openid_auth.store import DjangoOpenIDStore
        store = DjangoOpenIDStore()
    except:
        # otherwise use FileOpenIDStore
        OPENID_FILESTORE = '/tmp/openid-filestore'
        from openid.store.filestore import FileOpenIDStore
        store = FileOpenIDStore(OPENID_FILESTORE)
    server = Server(store, op_endpoint="%s%s" % (host, reverse('openid-provider-root')))
    # Clear AuthorizationInfo session var, if it is set
    if req.session.get('AuthorizationInfo', None):
        del req.session['AuthorizationInfo']
    querydict = dict(req.REQUEST.items())
    orequest = server.decodeRequest(querydict)
    if not orequest:
        orequest = req.session.get('OPENID_REQUEST', None)
        if not orequest:
            # not request, render info page:
            return render_to_response('openid_provider/server.html',
                {'host': host,},
                context_instance=RequestContext(req))
        else:
            # remove session stored data:
            del req.session['OPENID_REQUEST']
    if orequest.mode in ("checkid_immediate", "checkid_setup"):
        if not req.user.is_authenticated():
            return landing_page(req, orequest)
        #openid = openid_is_authorized(req, orequest.identity, orequest.trust_root)
        #if openid is not None:
        oresponse = orequest.answer(True, identity="%s%s" % (
                    host, reverse('openid-provider-identity', args=[req.user.username])))
        fullname = "%s %s" % (req.user.first_name, req.user.last_name)
        if fullname == " ":
            fullname = req.user.username
        if fullname.strip().find(" ") == -1:
            fullname = "%s %s" % (fullname, fullname)
        sreg_data = { 'fullname': fullname , 'email': req.user.email, 'nickname': req.user.username, }
        sreg_req = sreg.SRegRequest.fromOpenIDRequest(orequest)
        sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data)
        oresponse.addExtension(sreg_resp)
        #elif orequest.immediate:
        #    raise Exception('checkid_immediate mode not supported')
        #else:
        #    req.session['OPENID_REQUEST'] = orequest
        #    return HttpResponseRedirect(reverse('openid-provider-decide'))
    else:
        oresponse = server.handleRequest(orequest)
    webresponse = server.encodeResponse(oresponse)
    return django_response(webresponse)
Example #2
0
def endpoint():
    endpoint_url = url_for('.endpoint', _external=True)
    openid = OpenIDServer(OpenIDStore(), endpoint_url)
    query = request.args if request.method == 'GET' else request.form
    try:
        oid_req = openid.decodeRequest(query)
    except ProtocolError as e:
        return openid_exception(e)
    if not oid_req:
        return render_template('papersplease/openid/index.html')
    if oid_req.mode in ('checkid_immediate', 'checkid_setup'):
        if current_user.is_authenticated():
            return openid_response(openid, oid_req.answer(True))
        elif oid_req.immediate:
            return openid_response(openid, oid_req.answer(False))
        else:
            return login_manager.unauthorized()
    else:
        return openid_response(openid, openid.handleRequest(oid_req))
Example #3
0
def index():
    oidstore = Web2pyStore(db)
    cache.ram('oid_cleanup', lambda: oidstore.cleanup(), time_expire=25200) #call cleanup approx. every 7h
    oserver = Server(oidstore, OID_SERVER)
    orequest = oserver.decodeRequest(request.vars)
    
    #if another controller asked to cancel this authentication request do so
    if session.oid_cancel:
       session.oid_cancel = None
       return redirect(orequest.getCancelURL())
    
    user_is_logged_in = auth.is_logged_in()
    user_owns_id = user_is_logged_in and auth.user['nickname'] == orequest.identity.split('/').pop().lower() #lazy eval
    user_trusts_root = user_is_logged_in and _userTrustsRoot(orequest.trust_root) #lazy eval

    if orequest.mode in ['checkid_immediate', 'checkid_setup']:
        if user_is_logged_in and user_owns_id and user_trusts_root:
            resp = orequest.answer(True)
        elif orequest.immediate:
            resp = orequest.answer(False)
        else:
            #populate oid session with details as required by other controllers such as the url to
            #redirect to after logon or after approving data transfer to the relying party.
            session.oid_url = URL(r=request, args=request.args, vars=request.vars)
            #redirect user to authenticate herself or to authorise request 
            if user_is_logged_in:
                session.oid_orequest = orequest
                session.oid_user_owns_id = user_owns_id
                return redirect(URL(r=request, c='auth', f='data'))
            else:
                return redirect(URL(r=request, c='auth', f='user', args=['login']))            
    else:
        #let the library handle it
        resp = oserver.handleRequest(orequest)
    
    oresp = oserver.encodeResponse(resp)
    if oresp.code==200:
        return oresp.body
    else:
        return redirect(oresp.headers['location'], oresp.code)
Example #4
0
def provider_login(request):
    """
    OpenID login endpoint
    """

    # make and validate endpoint
    endpoint = get_xrds_url('login', request)
    if not endpoint:
        return default_render_failure(request, "Invalid OpenID request")

    # initialize store and server
    store = DjangoOpenIDStore()
    server = Server(store, endpoint)

    # first check to see if the request is an OpenID request.
    # If so, the client will have specified an 'openid.mode' as part
    # of the request.
    if request.method == 'GET':
        querydict = dict(request.GET.items())
    else:
        querydict = dict(request.POST.items())
    error = False
    if 'openid.mode' in request.GET or 'openid.mode' in request.POST:
        # decode request
        try:
            openid_request = server.decodeRequest(querydict)
        except (UntrustedReturnURL, ProtocolError):
            openid_request = None

        if not openid_request:
            return default_render_failure(request, "Invalid OpenID request")

        # don't allow invalid and non-trusted trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # checkid_immediate not supported, require user interaction
        if openid_request.mode == 'checkid_immediate':
            return provider_respond(server, openid_request,
                                    openid_request.answer(False), {})

        # checkid_setup, so display login page
        # (by falling through to the provider_login at the
        # bottom of this method).
        elif openid_request.mode == 'checkid_setup':
            if openid_request.idSelect():
                # remember request and original path
                request.session['openid_setup'] = {
                    'request': openid_request,
                    'url': request.get_full_path(),
                    'post_params': request.POST,
                }

                # user failed login on previous attempt
                if 'openid_error' in request.session:
                    error = True
                    del request.session['openid_error']

        # OpenID response
        else:
            return provider_respond(server, openid_request,
                                    server.handleRequest(openid_request), {})

    # handle login redirection:  these are also sent to this view function,
    # but are distinguished by lacking the openid mode.  We also know that
    # they are posts, because they come from the popup
    elif request.method == 'POST' and 'openid_setup' in request.session:
        # get OpenID request from session
        openid_setup = request.session['openid_setup']
        openid_request = openid_setup['request']
        openid_request_url = openid_setup['url']
        post_params = openid_setup['post_params']
        # We need to preserve the parameters, and the easiest way to do this is
        # through the URL
        url_post_params = {
            param: post_params[param] for param in post_params if param.startswith('openid')
        }

        encoded_params = urllib.urlencode(url_post_params)

        if '?' not in openid_request_url:
            openid_request_url = openid_request_url + '?' + encoded_params
        else:
            openid_request_url = openid_request_url + '&' + encoded_params

        del request.session['openid_setup']

        # don't allow invalid trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # check if user with given email exists
        # Failure is redirected to this method (by using the original URL),
        # which will bring up the login dialog.
        email = request.POST.get('email', None)
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            request.session['openid_error'] = True
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"OpenID login failed - Unknown user email")
            else:
                msg = u"OpenID login failed - Unknown user email: {0}".format(email)
                AUDIT_LOG.warning(msg)
            return HttpResponseRedirect(openid_request_url)

        # attempt to authenticate user (but not actually log them in...)
        # Failure is again redirected to the login dialog.
        username = user.username
        password = request.POST.get('password', None)
        try:
            user = authenticate(username=username, password=password, request=request)
        except RateLimitException:
            AUDIT_LOG.warning(u'OpenID - Too many failed login attempts.')
            return HttpResponseRedirect(openid_request_url)

        if user is None:
            request.session['openid_error'] = True
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"OpenID login failed - invalid password")
            else:
                AUDIT_LOG.warning(
                    u"OpenID login failed - password for %s is invalid", email)
            return HttpResponseRedirect(openid_request_url)

        # authentication succeeded, so fetch user information
        # that was requested
        if user is not None and user.is_active:
            # remove error from session since login succeeded
            if 'openid_error' in request.session:
                del request.session['openid_error']

            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.info(u"OpenID login success - user.id: %s", user.id)
            else:
                AUDIT_LOG.info(
                    u"OpenID login success - %s (%s)", user.username, user.email)
            # redirect user to return_to location
            url = endpoint + urlquote(user.username)
            response = openid_request.answer(True, None, url)

            # Note too that this is hardcoded, and not really responding to
            # the extensions that were registered in the first place.
            results = {
                'nickname': user.username,
                'email': user.email,
                'fullname': user.profile.name,
            }

            # the request succeeded:
            return provider_respond(server, openid_request, response, results)

        # the account is not active, so redirect back to the login page:
        request.session['openid_error'] = True
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            AUDIT_LOG.warning(
                u"Login failed - Account not active for user.id %s", user.id)
        else:
            AUDIT_LOG.warning(
                u"Login failed - Account not active for user %s", username)
        return HttpResponseRedirect(openid_request_url)

    # determine consumer domain if applicable
    return_to = request.GET.get('openid.return_to') or request.POST.get('openid.return_to') or ''
    if return_to:
        matches = re.match(r'\w+:\/\/([\w\.-]+)', return_to)
        return_to = matches.group(1)

    # display login page
    response = render_to_response('provider_login.html', {
        'error': error,
        'return_to': return_to
    })

    # add custom XRDS header necessary for discovery process
    response['X-XRDS-Location'] = get_xrds_url('xrds', request)
    return response
Example #5
0
class OpenIDProvider(object):
    # OpenID service type URIs, listed in order of preference.  The
    # ordering of this list affects yadis and XRI service discovery.
    openid_type_uris = [
        #discover.OPENID_IDP_2_0_TYPE,
        discover.OPENID_2_0_TYPE,
        discover.OPENID_1_1_TYPE,
        discover.OPENID_1_0_TYPE,
        sreg.ns_uri,
    ]

    def __init__(self, request):
        self.request = request
        self.openid_server = Server(SFOpenIDStore(self.request.db_session),
                                    request.route_url('openid_provider'))
        userid = authenticated_userid(request)
        self.auth_userid = userid or request.session.get('auth_userid')
        if self.auth_userid:
            query = self.request.db_session.query(UserProfile)
            self.auth_user = query.get(self.auth_userid)
        else:
            self.auth_user = None

    def process(self, request_params):
        logger.debug('Processing openid request: %s', request_params)
        if request_params.get('openid.identity'):
            home_url = self.request.route_url('home')
            if request_params['openid.identity'] == request_params['openid.claimed_id'] and \
               request_params['openid.identity'] == home_url:
                username = self.request.user and self.request.user.username or \
                           self.auth_userid
                if username:
                    username = username.lower()
                    user_url = self.request.route_url('user_profile',
                                                      username=username)
                    request_params['openid.identity'] = user_url
                    request_params['openid.claimed_id'] = user_url
        try:
            openid_request = self.openid_server.decodeRequest(request_params)
        # MalformedReturnURL, UntrustedReturnURL, etc.
        except ProtocolError as e:
            logger.info(
                'Unable to decode request: %s %s',
                e.__class__,
                e.openid_message
            )
            return ''
        logger.debug('Decoded request: %s', openid_request)

        if openid_request is None:
            return ''

        if openid_request.mode in ["checkid_immediate", "checkid_setup"]:
            if self.request.user or self.auth_userid:
                openid_response = self.handleCheckIDRequest(openid_request)
            else:
                # If the user has not logged in yet, stash the OpenID
                # consuming site request (if there isn't one already) and
                # send them to the login view. The openid_tween will take
                # care of sending them back to the OpenID consuming site.
                rp_dict = dict(request_params.items())
                self.request.session['openid_request'] = rp_dict
                self.request.session.save()
                return HTTPFound(location=self.request.route_url('login'))
        else:
            openid_response = self.openid_server.handleRequest(openid_request)

        logger.debug('Decoded response: %s', openid_response)
        encoded_response = self.openid_server.encodeResponse(openid_response)

        if 'location' in encoded_response.headers:
            response = HTTPFound(location=encoded_response.headers['location'])
            return response
        return encoded_response.body

    def get(self):
        return self.process(self.request.GET)

    def post(self):
        if self.request.method != "POST":
            return HTTPMethodNotAllowed()
        return self.process(self.request.POST)

    def approved(self, request, identifier=None):
        response = request.answer(True, identity=identifier)
        self.addSRegResponse(request, response)
        return response

    def handleCheckIDRequest(self, request):
        is_authorized = self.isAuthorized(request.identity, request.trust_root)
        if is_authorized:
            return self.approved(request, request.identity)
        else:
            return request.answer(False, identity=request.identity)

    def isAuthorized(self, identity_url, trust_root):
        if not self.auth_userid:
            return False

        self.request.registry.notify(CheckIDAuthorized(self.request,
                                                       self.auth_user))
        # TODO: prompt user for authorization
        return True

    def addSRegResponse(self, request, response):
        sreg_req = sreg.SRegRequest.fromOpenIDRequest(request)
        sreg_data = dict([
            (fname, None)
            for fname in sreg.data_fields
        ])
        sreg_data['fullname'] = '%s %s' % (self.auth_user.first_name,
                                           self.auth_user.last_name)
        sreg_data['nickname'] = self.auth_userid
        sreg_data['email'] = self.auth_user.email
        sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data)
        response.addExtension(sreg_resp)

    def identity(self):
        query = self.request.db_session.query(UserProfile)
        target_username = self.request.matchdict['username']
        target_user = query.get(target_username)
        if target_user is None:
            raise HTTPNotFound()
        return {
            'username': target_user.username.lower(),
        }
Example #6
0
def provider_login(request):
    """
    OpenID login endpoint
    """
    # pylint: disable=too-many-statements
    # make and validate endpoint
    endpoint = get_xrds_url('login', request)
    if not endpoint:
        return default_render_failure(request, "Invalid OpenID request")

    # initialize store and server
    store = DjangoOpenIDStore()
    server = Server(store, endpoint)

    # first check to see if the request is an OpenID request.
    # If so, the client will have specified an 'openid.mode' as part
    # of the request.
    if request.method == 'GET':
        querydict = dict(request.GET.items())
    else:
        querydict = dict(request.POST.items())
    error = False
    if 'openid.mode' in request.GET or 'openid.mode' in request.POST:
        # decode request
        try:
            openid_request = server.decodeRequest(querydict)
        except (UntrustedReturnURL, ProtocolError):
            openid_request = None

        if not openid_request:
            return default_render_failure(request, "Invalid OpenID request")

        # don't allow invalid and non-trusted trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # checkid_immediate not supported, require user interaction
        if openid_request.mode == 'checkid_immediate':
            return provider_respond(server, openid_request,
                                    openid_request.answer(False), {})

        # checkid_setup, so display login page
        # (by falling through to the provider_login at the
        # bottom of this method).
        elif openid_request.mode == 'checkid_setup':
            if openid_request.idSelect():
                # remember request and original path
                request.session['openid_setup'] = {
                    'request': openid_request,
                    'url': request.get_full_path(),
                    'post_params': request.POST,
                }

                # user failed login on previous attempt
                if 'openid_error' in request.session:
                    error = True
                    del request.session['openid_error']

        # OpenID response
        else:
            return provider_respond(server, openid_request,
                                    server.handleRequest(openid_request), {})

    # handle login redirection:  these are also sent to this view function,
    # but are distinguished by lacking the openid mode.  We also know that
    # they are posts, because they come from the popup
    elif request.method == 'POST' and 'openid_setup' in request.session:
        # get OpenID request from session
        openid_setup = request.session['openid_setup']
        openid_request = openid_setup['request']
        openid_request_url = openid_setup['url']
        post_params = openid_setup['post_params']
        # We need to preserve the parameters, and the easiest way to do this is
        # through the URL
        url_post_params = {
            param: post_params[param] for param in post_params if param.startswith('openid')
        }

        encoded_params = urllib.urlencode(url_post_params)

        if '?' not in openid_request_url:
            openid_request_url = openid_request_url + '?' + encoded_params
        else:
            openid_request_url = openid_request_url + '&' + encoded_params

        del request.session['openid_setup']

        # don't allow invalid trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # check if user with given email exists
        # Failure is redirected to this method (by using the original URL),
        # which will bring up the login dialog.
        email = request.POST.get('email', None)
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            request.session['openid_error'] = True
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"OpenID login failed - Unknown user email")
            else:
                msg = u"OpenID login failed - Unknown user email: {0}".format(email)
                AUDIT_LOG.warning(msg)
            return HttpResponseRedirect(openid_request_url)

        # attempt to authenticate user (but not actually log them in...)
        # Failure is again redirected to the login dialog.
        username = user.username
        password = request.POST.get('password', None)
        try:
            user = authenticate(username=username, password=password, request=request)
        except RateLimitException:
            AUDIT_LOG.warning(u'OpenID - Too many failed login attempts.')
            return HttpResponseRedirect(openid_request_url)

        if user is None:
            request.session['openid_error'] = True
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"OpenID login failed - invalid password")
            else:
                AUDIT_LOG.warning(
                    u"OpenID login failed - password for %s is invalid", email)
            return HttpResponseRedirect(openid_request_url)

        # authentication succeeded, so fetch user information
        # that was requested
        if user is not None and user.is_active:
            # remove error from session since login succeeded
            if 'openid_error' in request.session:
                del request.session['openid_error']

            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.info(u"OpenID login success - user.id: %s", user.id)
            else:
                AUDIT_LOG.info(
                    u"OpenID login success - %s (%s)", user.username, user.email)
            # redirect user to return_to location
            url = endpoint + urlquote(user.username)
            response = openid_request.answer(True, None, url)

            # Note too that this is hardcoded, and not really responding to
            # the extensions that were registered in the first place.
            results = {
                'nickname': user.username,
                'email': user.email,
                'fullname': user.profile.name,
            }

            # the request succeeded:
            return provider_respond(server, openid_request, response, results)

        # the account is not active, so redirect back to the login page:
        request.session['openid_error'] = True
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            AUDIT_LOG.warning(
                u"Login failed - Account not active for user.id %s", user.id)
        else:
            AUDIT_LOG.warning(
                u"Login failed - Account not active for user %s", username)
        return HttpResponseRedirect(openid_request_url)

    # determine consumer domain if applicable
    return_to = request.GET.get('openid.return_to') or request.POST.get('openid.return_to') or ''
    if return_to:
        matches = re.match(r'\w+:\/\/([\w\.-]+)', return_to)
        return_to = matches.group(1)

    # display login page
    response = render_to_response('provider_login.html', {
        'error': error,
        'return_to': return_to
    })

    # add custom XRDS header necessary for discovery process
    response['X-XRDS-Location'] = get_xrds_url('xrds', request)
    return response
class StubOpenIDProvider(HTTPFetcher):

    def __init__(self, base_url):
        self.store = MemoryStore()
        self.identity_url = base_url + 'identity'
        self.localid_url = base_url + 'localid'
        self.endpoint_url = base_url + 'endpoint'
        self.server = Server(self.store, self.endpoint_url)
        self.last_request = None
        self.type_uris = ['http://specs.openid.net/auth/2.0/signon']

    def fetch(self, url, body=None, headers=None):
        if url == self.identity_url:
            # Serve an XRDS document directly, pointing at our endpoint.
            type_uris = ['<Type>%s</Type>' % uri for uri in self.type_uris]
            return HTTPResponse(
                url, 200, {'content-type': 'application/xrds+xml'}, """\
<?xml version="1.0"?>
<xrds:XRDS
    xmlns="xri://$xrd*($v*2.0)"
    xmlns:xrds="xri://$xrds">
  <XRD>
    <Service priority="0">
      %s
      <URI>%s</URI>
      <LocalID>%s</LocalID>
    </Service>
  </XRD>
</xrds:XRDS>
""" % ('\n'.join(type_uris), self.endpoint_url, self.localid_url))
        elif url.startswith(self.endpoint_url):
            # Gather query parameters
            query = {}
            if '?' in url:
                query.update(cgi.parse_qsl(url.split('?', 1)[1]))
            if body is not None:
                query.update(cgi.parse_qsl(body))
            self.last_request = self.server.decodeRequest(query)

            # The browser based requests should not be handled through
            # the fetcher interface.
            assert self.last_request.mode not in BROWSER_REQUEST_MODES

            response = self.server.handleRequest(self.last_request)
            webresponse = self.server.encodeResponse(response)
            return HTTPResponse(url,  webresponse.code, webresponse.headers,
                                webresponse.body)
        else:
            raise HTTPFetchingError('unknown URL %s' % url)

    def parseFormPost(self, content):
        """Parse an HTML form post to create an OpenID request."""
        # Hack to make the javascript XML compliant ...
        content = content.replace('i < elements.length',
                                  'i &lt; elements.length')
        tree = ET.XML(content)
        form = tree.find('.//form')
        assert form is not None, 'No form in document'
        assert form.get('action') == self.endpoint_url, (
            'Form posts to %s instead of %s' % (form.get('action'),
                                                self.endpoint_url))
        query = {}
        for input in form.findall('input'):
            if input.get('type') != 'hidden':
                continue
            query[input.get('name').encode('UTF-8')] = \
                input.get('value').encode('UTF-8')
        self.last_request = self.server.decodeRequest(query)
        return self.last_request
Example #8
0
def provider_login(request):
    """
    OpenID login endpoint
    """

    # make and validate endpoint
    endpoint = get_xrds_url('login', request)
    if not endpoint:
        return default_render_failure(request, "Invalid OpenID request")

    # initialize store and server
    store = DjangoOpenIDStore()
    server = Server(store, endpoint)

    # first check to see if the request is an OpenID request.
    # If so, the client will have specified an 'openid.mode' as part
    # of the request.
    querydict = dict(request.REQUEST.items())
    error = False
    if 'openid.mode' in request.GET or 'openid.mode' in request.POST:
        # decode request
        try:
            openid_request = server.decodeRequest(querydict)
        except (UntrustedReturnURL, ProtocolError):
            openid_request = None

        if not openid_request:
            return default_render_failure(request, "Invalid OpenID request")

        # don't allow invalid and non-trusted trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # checkid_immediate not supported, require user interaction
        if openid_request.mode == 'checkid_immediate':
            return provider_respond(server, openid_request,
                                    openid_request.answer(False), {})

        # checkid_setup, so display login page
        # (by falling through to the provider_login at the
        # bottom of this method).
        elif openid_request.mode == 'checkid_setup':
            if openid_request.idSelect():
                # remember request and original path
                request.session['openid_setup'] = {
                    'request': openid_request,
                    'url': request.get_full_path()
                }

                # user failed login on previous attempt
                if 'openid_error' in request.session:
                    error = True
                    del request.session['openid_error']

        # OpenID response
        else:
            return provider_respond(server, openid_request,
                                    server.handleRequest(openid_request), {})

    # handle login redirection:  these are also sent to this view function,
    # but are distinguished by lacking the openid mode.  We also know that
    # they are posts, because they come from the popup
    elif request.method == 'POST' and 'openid_setup' in request.session:
        # get OpenID request from session
        openid_setup = request.session['openid_setup']
        openid_request = openid_setup['request']
        openid_request_url = openid_setup['url']
        del request.session['openid_setup']

        # don't allow invalid trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # check if user with given email exists
        # Failure is redirected to this method (by using the original URL),
        # which will bring up the login dialog.
        email = request.POST.get('email', None)
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            request.session['openid_error'] = True
            msg = "OpenID login failed - Unknown user email: %s"
            AUDIT_LOG.warning(msg, email)
            return HttpResponseRedirect(openid_request_url)

        # attempt to authenticate user (but not actually log them in...)
        # Failure is again redirected to the login dialog.
        username = user.username
        password = request.POST.get('password', None)
        try:
            user = authenticate(username=username, password=password, request=request)
        except RateLimitException:
            AUDIT_LOG.warning('OpenID - Too many failed login attempts.')
            return HttpResponseRedirect(openid_request_url)

        if user is None:
            request.session['openid_error'] = True
            msg = "OpenID login failed - password for %s is invalid"
            AUDIT_LOG.warning(msg, email)
            return HttpResponseRedirect(openid_request_url)

        # authentication succeeded, so fetch user information
        # that was requested
        if user is not None and user.is_active:
            # remove error from session since login succeeded
            if 'openid_error' in request.session:
                del request.session['openid_error']

            AUDIT_LOG.info("OpenID login success - %s (%s)",
                           user.username, user.email)

            # redirect user to return_to location
            url = endpoint + urlquote(user.username)
            response = openid_request.answer(True, None, url)

            # TODO: for CS50 we are forcibly returning the username
            # instead of fullname. In the OpenID simple registration
            # extension, we don't have to return any fields we don't
            # want to, even if they were marked as required by the
            # Consumer. The behavior of what to do when there are
            # missing fields is up to the Consumer. The proper change
            # should only return the username, however this will likely
            # break the CS50 client. Temporarily we will be returning
            # username filling in for fullname in addition to username
            # as sreg nickname.

            # Note too that this is hardcoded, and not really responding to
            # the extensions that were registered in the first place.
            results = {
                'nickname': user.username,
                'email': user.email,
                'fullname': user.username
            }

            # the request succeeded:
            return provider_respond(server, openid_request, response, results)

        # the account is not active, so redirect back to the login page:
        request.session['openid_error'] = True
        msg = "Login failed - Account not active for user %s"
        AUDIT_LOG.warning(msg, username)
        return HttpResponseRedirect(openid_request_url)

    # determine consumer domain if applicable
    return_to = ''
    if 'openid.return_to' in request.REQUEST:
        return_to = request.REQUEST['openid.return_to']
        matches = re.match(r'\w+:\/\/([\w\.-]+)', return_to)
        return_to = matches.group(1)

    # display login page
    response = render_to_response('provider_login.html', {
        'error': error,
        'return_to': return_to
    })

    # add custom XRDS header necessary for discovery process
    response['X-XRDS-Location'] = get_xrds_url('xrds', request)
    return response
Example #9
0
def openid_server(req):
    """
    This view is the actual OpenID server - running at the URL pointed to by
    the <link rel="openid.server"> tag.
    """
    host = get_base_uri(req)
    try:
        # if we have django_openid_auth in applications directory
        # then we can use DjangoOpenIDStore
        from django_openid_auth.store import DjangoOpenIDStore
        store = DjangoOpenIDStore()
    except:
        # otherwise use FileOpenIDStore
        OPENID_FILESTORE = '/tmp/openid-filestore'
        from openid.store.filestore import FileOpenIDStore
        store = FileOpenIDStore(OPENID_FILESTORE)

    server = Server(store,
                    op_endpoint="%s%s" %
                    (host, reverse('openid-provider-root')))

    # Clear AuthorizationInfo session var, if it is set
    if req.session.get('AuthorizationInfo', None):
        del req.session['AuthorizationInfo']

    querydict = dict(req.REQUEST.items())
    try:
        orequest = server.decodeRequest(querydict)
    except:
        L.exception("Request decode failed")
        orequest = None
    if not orequest:
        orequest = req.session.get('OPENID_REQUEST', None)
        if not orequest:
            # not request, render info page:
            return render_to_response('openid_provider/server.html', {
                'host': host,
            },
                                      context_instance=RequestContext(req))
        else:
            # remove session stored data:
            del req.session['OPENID_REQUEST']

    if orequest.mode in ("checkid_immediate", "checkid_setup"):

        if not req.user.is_authenticated():
            return landing_page(req, orequest)

        openid = openid_is_authorized(req, orequest.identity,
                                      orequest.trust_root)

        if openid is not None:
            oresponse = orequest.answer(
                True,
                identity="%s%s" %
                (host, reverse('openid-provider-identity',
                               args=[openid.openid])))

            sreg_data = {'nickname': req.user.username}
            sreg_req = sreg.SRegRequest.fromOpenIDRequest(orequest)
            sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data)
            oresponse.addExtension(sreg_resp)

        elif orequest.immediate:
            raise Exception('checkid_immediate mode not supported')
        else:
            req.session['OPENID_REQUEST'] = orequest
            return HttpResponseRedirect(reverse('openid-provider-decide'))
    else:
        oresponse = server.handleRequest(orequest)
    webresponse = server.encodeResponse(oresponse)
    return django_response(webresponse)
Example #10
0
def openid_server(request):
    """
    This view is the actual OpenID server - running at the URL pointed to by
    the <link rel="openid.server"> tag.
    """
    logger.debug('server request %s: %s',
                 request.method, request.POST or request.GET)
    server = Server(get_store(request),
        op_endpoint=request.build_absolute_uri(reverse('openid-provider-root')))

    if not request.is_secure():
        # if request is not secure allow only encrypted association sessions
        server.negotiator = encrypted_negotiator

    # Clear AuthorizationInfo session var, if it is set
    if request.session.get('AuthorizationInfo', None):
        del request.session['AuthorizationInfo']

    querydict = dict(request.POST.items())
    orequest = server.decodeRequest(querydict)
    if not orequest:
        orequest = request.session.get('OPENID_REQUEST', None)
        if orequest:
            # remove session stored data:
            del request.session['OPENID_REQUEST']
        else:
            # not request, render info page:
            data = {
                'host': request.build_absolute_uri('/'),
                'xrds_location': request.build_absolute_uri(
                    reverse('openid-provider-xrds')),
            }
            # Return empty string
            return HttpResponse("", content_type="text/plain")

    if orequest.mode in BROWSER_REQUEST_MODES:
        if not request.user.is_authenticated:
            logger.debug('no local authentication, sending landing page')
            return landing_page(request, orequest)

        openid = openid_is_authorized(request, orequest.identity,
                                      orequest.trust_root)

        if openid is not None:
            id_url = request.build_absolute_uri(
                reverse('openid-provider-identity', args=[openid.openid]))
            oresponse = orequest.answer(True, identity=id_url)
            logger.debug('orequest.answer(True, identity="%s")', id_url)
        elif orequest.immediate:
            logger.debug('checkid_immediate mode not supported')
            raise Exception('checkid_immediate mode not supported')
        else:
            request.session['OPENID_REQUEST'] = orequest
            logger.debug('redirecting to decide page')
            return HttpResponseRedirect(reverse('openid-provider-decide'))
    else:
        oresponse = server.handleRequest(orequest)
    if request.user.is_authenticated:
        add_sreg_data(request, orequest, oresponse)
        if conf.AX_EXTENSION:
            add_ax_data(request, orequest, oresponse)
    # Convert a webresponse from the OpenID library in to a Django HttpResponse
    webresponse = server.encodeResponse(oresponse)
    if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES:
        response = render(request, 'openid_provider/response.html', {
            'body': webresponse.body,
        })
        logger.debug('rendering browser response')
    else:
        response = HttpResponse(webresponse.body)
        response.status_code = webresponse.code
        for key, value in webresponse.headers.items():
            response[key] = value
        logger.debug('rendering raw response')
    return response
Example #11
0
class StubOpenIDProvider(HTTPFetcher):
    def __init__(self, base_url):
        self.store = MemoryStore()
        self.identity_url = base_url + 'identity'
        self.localid_url = base_url + 'localid'
        self.endpoint_url = base_url + 'endpoint'
        self.server = Server(self.store, self.endpoint_url)
        self.last_request = None
        self.type_uris = ['http://specs.openid.net/auth/2.0/signon']

    def fetch(self, url, body=None, headers=None):
        if url == self.identity_url:
            # Serve an XRDS document directly, pointing at our endpoint.
            type_uris = ['<Type>%s</Type>' % uri for uri in self.type_uris]
            return HTTPResponse(
                url, 200, {'content-type': 'application/xrds+xml'}, """\
<?xml version="1.0"?>
<xrds:XRDS
    xmlns="xri://$xrd*($v*2.0)"
    xmlns:xrds="xri://$xrds">
  <XRD>
    <Service priority="0">
      %s
      <URI>%s</URI>
      <LocalID>%s</LocalID>
    </Service>
  </XRD>
</xrds:XRDS>
""" % ('\n'.join(type_uris), self.endpoint_url, self.localid_url))
        elif url.startswith(self.endpoint_url):
            # Gather query parameters
            query = {}
            if '?' in url:
                query.update(cgi.parse_qsl(url.split('?', 1)[1]))
            if body is not None:
                query.update(cgi.parse_qsl(body))
            self.last_request = self.server.decodeRequest(query)

            # The browser based requests should not be handled through
            # the fetcher interface.
            assert self.last_request.mode not in BROWSER_REQUEST_MODES

            response = self.server.handleRequest(self.last_request)
            webresponse = self.server.encodeResponse(response)
            return HTTPResponse(url, webresponse.code, webresponse.headers,
                                webresponse.body)
        else:
            raise HTTPFetchingError('unknown URL %s' % url)

    def parseFormPost(self, content):
        """Parse an HTML form post to create an OpenID request."""
        # Hack to make the javascript XML compliant ...
        content = content.replace('i < elements.length',
                                  'i &lt; elements.length')
        tree = ET.XML(content)
        form = tree.find('.//form')
        assert form is not None, 'No form in document'
        assert form.get('action') == self.endpoint_url, (
            'Form posts to %s instead of %s' %
            (form.get('action'), self.endpoint_url))
        query = {}
        for input in form.findall('input'):
            if input.get('type') != 'hidden':
                continue
            query[input.get('name').encode('UTF-8')] = \
                input.get('value').encode('UTF-8')
        self.last_request = self.server.decodeRequest(query)
        return self.last_request
Example #12
0
def provider_login(request):
    """
    OpenID login endpoint
    """

    # make and validate endpoint
    endpoint = get_xrds_url("login", request)
    if not endpoint:
        return default_render_failure(request, "Invalid OpenID request")

    # initialize store and server
    store = DjangoOpenIDStore()
    server = Server(store, endpoint)

    # first check to see if the request is an OpenID request.
    # If so, the client will have specified an 'openid.mode' as part
    # of the request.
    querydict = dict(request.REQUEST.items())
    error = False
    if "openid.mode" in request.GET or "openid.mode" in request.POST:
        # decode request
        try:
            openid_request = server.decodeRequest(querydict)
        except (UntrustedReturnURL, ProtocolError):
            openid_request = None

        if not openid_request:
            return default_render_failure(request, "Invalid OpenID request")

        # don't allow invalid and non-trusted trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # checkid_immediate not supported, require user interaction
        if openid_request.mode == "checkid_immediate":
            return provider_respond(server, openid_request, openid_request.answer(False), {})

        # checkid_setup, so display login page
        # (by falling through to the provider_login at the
        # bottom of this method).
        elif openid_request.mode == "checkid_setup":
            if openid_request.idSelect():
                # remember request and original path
                request.session["openid_setup"] = {"request": openid_request, "url": request.get_full_path()}

                # user failed login on previous attempt
                if "openid_error" in request.session:
                    error = True
                    del request.session["openid_error"]

        # OpenID response
        else:
            return provider_respond(server, openid_request, server.handleRequest(openid_request), {})

    # handle login redirection:  these are also sent to this view function,
    # but are distinguished by lacking the openid mode.  We also know that
    # they are posts, because they come from the popup
    elif request.method == "POST" and "openid_setup" in request.session:
        # get OpenID request from session
        openid_setup = request.session["openid_setup"]
        openid_request = openid_setup["request"]
        openid_request_url = openid_setup["url"]
        del request.session["openid_setup"]

        # don't allow invalid trust roots
        if not validate_trust_root(openid_request):
            return default_render_failure(request, "Invalid OpenID trust root")

        # check if user with given email exists
        # Failure is redirected to this method (by using the original URL),
        # which will bring up the login dialog.
        email = request.POST.get("email", None)
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            request.session["openid_error"] = True
            if settings.FEATURES["SQUELCH_PII_IN_LOGS"]:
                AUDIT_LOG.warning("OpenID login failed - Unknown user email")
            else:
                msg = "OpenID login failed - Unknown user email: {0}".format(email)
                AUDIT_LOG.warning(msg)
            return HttpResponseRedirect(openid_request_url)

        # attempt to authenticate user (but not actually log them in...)
        # Failure is again redirected to the login dialog.
        username = user.username
        password = request.POST.get("password", None)
        try:
            user = authenticate(username=username, password=password, request=request)
        except RateLimitException:
            AUDIT_LOG.warning("OpenID - Too many failed login attempts.")
            return HttpResponseRedirect(openid_request_url)

        if user is None:
            request.session["openid_error"] = True
            if settings.FEATURES["SQUELCH_PII_IN_LOGS"]:
                AUDIT_LOG.warning("OpenID login failed - invalid password")
            else:
                msg = "OpenID login failed - password for {0} is invalid".format(email)
                AUDIT_LOG.warning(msg)
            return HttpResponseRedirect(openid_request_url)

        # authentication succeeded, so fetch user information
        # that was requested
        if user is not None and user.is_active:
            # remove error from session since login succeeded
            if "openid_error" in request.session:
                del request.session["openid_error"]

            if settings.FEATURES["SQUELCH_PII_IN_LOGS"]:
                AUDIT_LOG.info("OpenID login success - user.id: {0}".format(user.id))
            else:
                AUDIT_LOG.info("OpenID login success - {0} ({1})".format(user.username, user.email))

            # redirect user to return_to location
            url = endpoint + urlquote(user.username)
            response = openid_request.answer(True, None, url)

            # Note too that this is hardcoded, and not really responding to
            # the extensions that were registered in the first place.
            results = {"nickname": user.username, "email": user.email, "fullname": user.profile.name}

            # the request succeeded:
            return provider_respond(server, openid_request, response, results)

        # the account is not active, so redirect back to the login page:
        request.session["openid_error"] = True
        if settings.FEATURES["SQUELCH_PII_IN_LOGS"]:
            AUDIT_LOG.warning("Login failed - Account not active for user.id {0}".format(user.id))
        else:
            msg = "Login failed - Account not active for user {0}".format(username)
            AUDIT_LOG.warning(msg)
        return HttpResponseRedirect(openid_request_url)

    # determine consumer domain if applicable
    return_to = ""
    if "openid.return_to" in request.REQUEST:
        return_to = request.REQUEST["openid.return_to"]
        matches = re.match(r"\w+:\/\/([\w\.-]+)", return_to)
        return_to = matches.group(1)

    # display login page
    response = render_to_response("provider_login.html", {"error": error, "return_to": return_to})

    # add custom XRDS header necessary for discovery process
    response["X-XRDS-Location"] = get_xrds_url("xrds", request)
    return response
Example #13
0
def openid_server(request):
    """
    This view is the actual OpenID server - running at the URL pointed to by 
    the <link rel="openid.server"> tag. 
    """
    logger.debug('server request %s: %s', request.method, request.POST
                 or request.GET)
    server = Server(get_store(request),
                    op_endpoint=request.build_absolute_uri(
                        reverse('openid-provider-root')))

    if not request.is_secure():
        # if request is not secure allow only encrypted association sessions
        server.negotiator = encrypted_negotiator

    # Clear AuthorizationInfo session var, if it is set
    if request.session.get('AuthorizationInfo', None):
        del request.session['AuthorizationInfo']

    querydict = dict(request.REQUEST.items())
    orequest = server.decodeRequest(querydict)
    if not orequest:
        orequest = request.session.get('OPENID_REQUEST', None)
        if orequest:
            # remove session stored data:
            del request.session['OPENID_REQUEST']
        else:
            # not request, render info page:
            data = {
                'host':
                request.build_absolute_uri('/'),
                'xrds_location':
                request.build_absolute_uri(reverse('openid-provider-xrds')),
            }
            logger.debug('invalid request, sending info: %s', data)
            return render_to_response('openid_provider/server.html',
                                      data,
                                      context_instance=RequestContext(request))

    if orequest.mode in BROWSER_REQUEST_MODES:
        if not request.user.is_authenticated():

            #return HttpResponse(orequest.return_to)
            logger.debug('no local authentication, sending landing page')
            return landing_page(request, orequest)

        openid = openid_is_authorized(request, orequest.identity,
                                      orequest.trust_root)

        if openid is not None:
            id_url = request.build_absolute_uri(
                reverse('openid-provider-identity', args=[openid.openid]))
            oresponse = orequest.answer(True, identity=id_url)
            logger.debug('orequest.answer(True, identity="%s")', id_url)
        elif orequest.immediate:
            logger.debug('checkid_immediate mode not supported')
            raise Exception('checkid_immediate mode not supported')
        else:
            request.session['OPENID_REQUEST'] = orequest
            logger.debug('redirecting to decide page')
            return HttpResponseRedirect(reverse('openid-provider-decide'))
    else:
        oresponse = server.handleRequest(orequest)
    if request.user.is_authenticated():
        add_sreg_data(request, orequest, oresponse)
        if conf.AX_EXTENSION:
            add_ax_data(request, orequest, oresponse)
    # Convert a webresponse from the OpenID library in to a Django HttpResponse
    webresponse = server.encodeResponse(oresponse)
    if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES:
        response = render_to_response('openid_provider/response.html', {
            'body': webresponse.body,
        },
                                      context_instance=RequestContext(request))
        logger.debug('rendering browser response')
    else:
        response = HttpResponse(webresponse.body)
        response.status_code = webresponse.code
        for key, value in webresponse.headers.items():
            response[key] = value
        logger.debug('rendering raw response')
    return response
Example #14
0
class OpenIDProvider(object):
    # OpenID service type URIs, listed in order of preference.  The
    # ordering of this list affects yadis and XRI service discovery.
    openid_type_uris = [
        #discover.OPENID_IDP_2_0_TYPE,
        discover.OPENID_2_0_TYPE,
        discover.OPENID_1_1_TYPE,
        discover.OPENID_1_0_TYPE,
        sreg.ns_uri,
    ]

    def __init__(self, request):
        self.request = request
        self.openid_server = Server(SFOpenIDStore(self.request.db_session),
                                    request.route_url('openid_provider'))
        userid = authenticated_userid(request)
        self.auth_userid = userid or request.session.get('auth_userid')
        if self.auth_userid:
            query = self.request.db_session.query(UserProfile)
            self.auth_user = query.get(self.auth_userid)
        else:
            self.auth_user = None

    def process(self, request_params):
        logger.debug('Processing openid request: %s', request_params)
        if request_params.get('openid.identity'):
            home_url = self.request.route_url('home')
            if request_params['openid.identity'] == request_params['openid.claimed_id'] and \
               request_params['openid.identity'] == home_url:
                username = self.request.user and self.request.user.username or \
                           self.auth_userid
                if username:
                    username = username.lower()
                    user_url = self.request.route_url('user_profile',
                                                      username=username)
                    request_params['openid.identity'] = user_url
                    request_params['openid.claimed_id'] = user_url
        try:
            openid_request = self.openid_server.decodeRequest(request_params)
        # MalformedReturnURL, UntrustedReturnURL, etc.
        except ProtocolError as e:
            logger.info('Unable to decode request: %s %s', e.__class__,
                        e.openid_message)
            return ''
        logger.debug('Decoded request: %s', openid_request)

        if openid_request is None:
            return ''

        if openid_request.mode in ["checkid_immediate", "checkid_setup"]:
            if self.request.user or self.auth_userid:
                openid_response = self.handleCheckIDRequest(openid_request)
            else:
                # If the user has not logged in yet, stash the OpenID
                # consuming site request (if there isn't one already) and
                # send them to the login view. The openid_tween will take
                # care of sending them back to the OpenID consuming site.
                rp_dict = dict(request_params.items())
                self.request.session['openid_request'] = rp_dict
                self.request.session.save()
                return HTTPFound(location=self.request.route_url('login'))
        else:
            openid_response = self.openid_server.handleRequest(openid_request)

        logger.debug('Decoded response: %s', openid_response)
        encoded_response = self.openid_server.encodeResponse(openid_response)

        if 'location' in encoded_response.headers:
            response = HTTPFound(location=encoded_response.headers['location'])
            return response
        return encoded_response.body

    def get(self):
        return self.process(self.request.GET)

    def post(self):
        if self.request.method != "POST":
            return HTTPMethodNotAllowed()
        return self.process(self.request.POST)

    def approved(self, request, identifier=None):
        response = request.answer(True, identity=identifier)
        self.addSRegResponse(request, response)
        return response

    def handleCheckIDRequest(self, request):
        is_authorized = self.isAuthorized(request.identity, request.trust_root)
        if is_authorized:
            return self.approved(request, request.identity)
        else:
            return request.answer(False, identity=request.identity)

    def isAuthorized(self, identity_url, trust_root):
        if not self.auth_userid:
            return False

        self.request.registry.notify(
            CheckIDAuthorized(self.request, self.auth_user))
        # TODO: prompt user for authorization
        return True

    def addSRegResponse(self, request, response):
        sreg_req = sreg.SRegRequest.fromOpenIDRequest(request)
        sreg_data = dict([(fname, None) for fname in sreg.data_fields])
        sreg_data['fullname'] = '%s %s' % (self.auth_user.first_name,
                                           self.auth_user.last_name)
        sreg_data['nickname'] = self.auth_userid
        sreg_data['email'] = self.auth_user.email
        sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data)
        response.addExtension(sreg_resp)

    def identity(self):
        query = self.request.db_session.query(UserProfile)
        target_username = self.request.matchdict['username']
        target_user = query.get(target_username)
        if target_user is None:
            raise HTTPNotFound()
        return {
            'username': target_user.username.lower(),
        }