Ignore:
Timestamp:
10/05/12 15:33:15 (9 years ago)
Author:
rwilkinson
Message:

Added processing of MyProxy? certifcate requests as OAuth resource requests.
Added login form with Genshi rendering of all forms.
Added checking of scope in OAuth requests.

Location:
trunk/ndg_oauth/ndg_oauth_server
Files:
17 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/ndg_oauth/ndg_oauth_server/client_register.ini

    r8030 r8057  
    1010#id=477bfc8c-a739-45b3-a63b-5b1662cc12d7 
    1111type=confidential 
    12 redirect_uris=http://localhost:5001/client/redirect_target 
    13 authentication_data=/O=STFC/OU=BADC/OU=simpleCA-localhost/OU=badc.rl.ac.uk/CN=test1.client 
     12redirect_uris=http://ice.badc.rl.ac.uk:5001/client/redirect_target 
     13authentication_data=/O=STFC/OU=BADC/OU=simpleCA-ice.badc.rl.ac.uk/OU=badc.rl.ac.uk/CN=test1.client 
    1414 
    1515[client:test2] 
    16 name=test2 
     16name=WPS on Ice 
    1717id=22 
    1818#id=691ad8cc-293c-4fbe-b8eb-980fcd621157 
    1919type=confidential 
    20 redirect_uris=http://localhost:5002/oauth2/oauth_redirect 
    21 authentication_data=/O=STFC/OU=BADC/OU=simpleCA-localhost/OU=badc.rl.ac.uk/CN=test.client 
     20redirect_uris=http://ice.badc.rl.ac.uk:5002/oauth2/oauth_redirect,http://ice.badc.rl.ac.uk:5005/oauth2/oauth_redirect 
     21authentication_data=/O=STFC/OU=BADC/OU=simpleCA-ice.badc.rl.ac.uk/OU=badc.rl.ac.uk/CN=test.client 
  • trunk/ndg_oauth/ndg_oauth_server/development.ini

    r8030 r8057  
    1818host = 0.0.0.0 
    1919port = 5000 
    20 ssl_pem = /home/rwilkinson_local/dev/MyProxyWebService/host0.pem 
     20ssl_pem = %(here)s/host.pem 
    2121 
    2222[pipeline:main] 
    23 pipeline = BeakerSessionFilter repoze_who MyProxyClient OAuth2Authz OAuth2Server 
     23pipeline = BeakerSessionFilter 
     24           repoze_who 
     25           AuthnForm 
     26           MyProxyClient 
     27           OAuth2Authz 
     28           OAuth2ServerFilterApp 
     29#           OAuth2Server 
    2430 
    2531# This filter sets up a server side session linked to a cookie.  The session 
     
    3440environ_key = %(beakerSessionKeyName)s 
    3541beaker.session.secret = somesecret 
    36 beaker.cache.data_dir = %(here)s/authn/beaker/cache 
     42#beaker.cache.data_dir = %(here)s/authn/beaker/cache 
     43beaker.session.type = file 
    3744beaker.session.data_dir = %(here)s/authn/beaker/sessions 
    3845 
     
    4249log_file = stdout 
    4350log_level = debug 
     51 
     52[filter:AuthnForm] 
     53paste.filter_app_factory = ndg.oauth.server.wsgi.authentication_filter:AuthenticationFormMiddleware.filter_app_factory 
     54authenticationForm.base_url_path = /authentication 
     55authenticationForm.client_register=%(here)s/client_register.ini 
     56# If true, client authorization included on login form, otherwise the separate 
     57# client authorization form is always used. 
     58authenticationForm.combined_authorization = True 
     59authenticationForm.login_cancelled = %(here)s/ndg/oauth/server/templates/login_cancelled.html 
     60authenticationForm.login_form = %(here)s/ndg/oauth/server/templates/login_form.html 
     61authenticationForm.return_url_param = returnurl 
     62authenticationForm.session_key_name = %(beakerSessionKeyName)s 
     63# Authentication form configuration 
     64authenticationForm.layout.heading = OAuth Login 
     65authenticationForm.layout.title = OAuth Login 
     66authenticationForm.layout.rightLink = http://ceda.ac.uk/ 
     67authenticationForm.layout.rightImage = /oas/layout/CEDA_RightButton60.png 
     68#authenticationForm.layout.rightImage = /layout/CEDA_RightButton60.png 
     69authenticationForm.layout.rightAlt = Centre for Environmental Data Archival 
     70authenticationForm.layout.footerText = This site is for test purposes only. 
     71authenticationForm.layout.helpIcon = /oas/layout/icons/help.png 
     72#authenticationForm.layout.helpIcon = /layout/icons/help.png 
    4473 
    4574[filter:MyProxyClient] 
     
    5180# fully qualified domain name or else set the MYPROXY_SERVER environment 
    5281# variable.  See the documentation for the MyProxyClient egg for details 
    53 myproxy.client.hostname = localhost 
     82myproxy.client.hostname = myproxy.ac.uk 
    5483#myproxy.client.port = 7512 
    5584 
     
    5786# server that it fronts e.g. set to /etc/grid-security/certificates.  For these 
    5887# tests set to local ca directory 
    59 #MyProxy.client.caCertDir = %(here)s/ca 
    60 myproxy.client.caCertDir = /home/users/rwilkinson/.esg/certificates_ice 
     88myproxy.client.caCertDir = %(here)s/ca 
    6189 
    6290[filter:OAuth2Authz] 
     
    6997oauth2authorization.session_key_name = %(beakerSessionKeyName)s 
    7098#oauth2authorization.user_identifier_key=REMOTE_USER 
     99# Authorization form configuration 
     100oauth2authorization.layout.heading = OAuth Authorisation 
     101oauth2authorization.layout.title = OAuth Authorisation 
     102oauth2authorization.layout.rightLink = http://ceda.ac.uk/ 
     103oauth2authorization.layout.rightImage = /layout/CEDA_RightButton60.png 
     104oauth2authorization.layout.rightAlt = Centre for Environmental Data Archival 
     105oauth2authorization.layout.footerText = This site is for test purposes only. 
     106oauth2authorization.layout.helpIcon = /layout/icons/help.png 
    71107 
    72108[app:OAuth2Server] 
     
    75111# OAuth2 server configuration options - defaults are commented out. 
    76112#oauth2server.access_token_lifetime=86400 
    77 # Allowed values: myproxy (default) or bearer (which returns a UUID for testing purposes only) 
     113# Allowed values: myproxy (default) or bearer (which returns a UUID) 
    78114#oauth2server.access_token_type=myproxy 
     115oauth2server.access_token_type=bearer 
    79116#oauth2server.authorization_grant_lifetime=600 
    80117oauth2server.base_url_path=/oauth 
     
    82119# Allowed values: certificate (default) or none. 
    83120#oauth2server.client_authentication_method=certificate 
     121oauth2server.client_authentication_method=none 
    84122#oauth2server.client_authorization_url=client_authorization/authorize 
    85123#oauth2server.client_authorizations_key=client_authorizations 
    86124oauth2server.client_register=%(here)s/client_register.ini 
    87125#oauth2server.myproxy_client_key=myproxy.server.wsgi.middleware.MyProxyClientMiddleware.myProxyClient 
    88 oauth2server.myproxy_global_password=changeme 
     126oauth2server.myproxy_global_password=i93rRugz 
    89127#oauth2server.session_key_name=beaker.session.oauth2server 
    90128#oauth2server.user_identifier_key=REMOTE_USER 
     
    103141# data_dir is used if lock_dir not set: 
    104142#oauth2server.cache.authorizationgrantregister.lock_dir 
     143 
     144[filter-app:OAuth2ServerFilterApp] 
     145use = egg:Paste#httpexceptions 
     146next = cascade 
     147 
     148[composit:cascade] 
     149use = egg:Paste#cascade 
     150app1 = OAuth2Server 
     151app2 = StaticContent 
     152catch = 404 
     153 
     154[app:StaticContent] 
     155use = egg:Paste#static 
     156document_root = %(here)s/static 
    105157 
    106158# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/lib/authorization_server.py

    r8033 r8057  
    364364        return 
    365365 
    366     def check_token(self, request): 
     366    def check_token(self, request, scope=None): 
    367367        """ 
    368368        Simple service that could be used to validate bearer tokens. It would 
     
    388388        @param request: HTTP request object 
    389389 
     390        @type scope: str 
     391        @param scope: required scope 
     392 
    390393        @rtype: tuple: (str, int, str) 
    391394        @return: tuple ( 
    392395                     OAuth JSON response 
    393                      HTTP status if error 
     396                     HTTP status 
    394397                     error description 
    395398                 ) 
     
    399402            error = 'invalid_request' 
    400403        else: 
    401             access_token = request.params['access_token'] 
    402             scope = request.params.get('scope', None) 
    403             (token, error) = self.access_token_register.get_token(access_token, scope) 
    404          
     404            access_token = params['access_token'] 
     405            if scope: 
     406                required_scope = scope 
     407            else: 
     408                required_scope = params.get('scope', None) 
     409            (token, error) = self.access_token_register.get_token(access_token, 
     410                                                                required_scope) 
     411 
    405412        status = {'invalid_request': httplib.BAD_REQUEST, 
    406                   'invalid_token': httplib.UNAUTHORIZED, 
     413                  'invalid_token': httplib.FORBIDDEN, 
    407414                  None: httplib.OK}.get(error, httplib.BAD_REQUEST) 
    408415 
     
    411418            content_dict['error'] = error 
    412419        content = json.dumps(content_dict) 
    413         return (content, None, None) 
     420        return (content, status, error) 
     421 
     422    def get_registered_token(self, request, scope=None): 
     423        """ 
     424        Checks that a token in the request is valid. It would 
     425        be called from a resource service that trusts this authorization 
     426        service. This is not part of the OAuth specification. 
     427 
     428        Request parameters 
     429 
     430        access_token 
     431              REQUIRED.  Bearer token 
     432        scope 
     433              OPTIONAL.  Scope  
     434 
     435        Response: 
     436              application/json format: 
     437        status 
     438              HTTP status indicating the access control decision 
     439        error 
     440              error as described in 
     441              http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-5.2 
     442 
     443        @type request: webob.Request 
     444        @param request: HTTP request object 
     445 
     446        @type scope: str 
     447        @param scope: required scope 
     448 
     449        @rtype: tuple: (str, int, str) 
     450        @return: tuple ( 
     451                     access token 
     452                     HTTP status 
     453                     error description 
     454                 ) 
     455        """ 
     456        params = request.params 
     457        token = None 
     458        if 'access_token' not in params: 
     459            error = 'invalid_request' 
     460        else: 
     461            access_token = params['access_token'] 
     462            if scope: 
     463                required_scope = scope 
     464            else: 
     465                required_scope = params.get('scope', None) 
     466            (token, error) = self.access_token_register.get_token(access_token, 
     467                                                                required_scope) 
     468 
     469        status = {'invalid_request': httplib.BAD_REQUEST, 
     470                  'invalid_token': httplib.FORBIDDEN, 
     471                  'insufficient_scope': httplib.FORBIDDEN, 
     472                  None: httplib.OK}.get(error, httplib.BAD_REQUEST) 
     473 
     474        return (token, status, error) 
    414475 
    415476    def is_registered_client(self, request): 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/lib/register/access_token.py

    r8030 r8057  
    1212 
    1313from ndg.oauth.server.lib.register.register_base import RegisterBase 
     14import ndg.oauth.server.lib.register.scopeutil as scopeutil 
    1415 
    1516log = logging.getLogger(__name__) 
     
    2324        self.token_type = token_type 
    2425        self.grant = grant 
    25         self.scope = grant.scope.split() if grant.scope else [] 
     26        self.scope = scopeutil.scopeStringToList(grant.scope_str) 
    2627        self.timestamp = datetime.now() 
    2728        self.lifetime = lifetime 
     
    4142 
    4243    def add_token(self, token): 
     44        """Adds a token to the register. 
     45        @type token: AccessToken 
     46        @param token: access token 
     47        """ 
    4348        if self.has_key(token.token_id): 
    4449            # Internal error 
     
    4752 
    4853        self.set_value(token.token_id, token) 
     54        log.debug("Added token of ID: %s", token.token_id) 
    4955        return True 
    5056 
    5157    def get_token(self, token_id, scope): 
     58        """Retrieves a registered token by token ID and required scope. 
     59        @type token_id: basestring 
     60        @param token_id: token ID 
     61        @type scope: basestring 
     62        @param scope: required scopes as space separated string 
     63        """ 
    5264        try: 
    5365            token = self.get_value(token_id) 
    5466        except KeyError: 
     67            log.debug("Request for token of ID that is not registered: %s", 
     68                      token_id) 
    5569            return (None, 'invalid_token') 
    5670 
     
    6276            return (None, 'invalid_token') 
    6377        # Check scope 
    64         if scope and (scope not in token.scope): 
     78        if not scopeutil.isScopeGranted(token.scope, 
     79                                        scopeutil.scopeStringToList(scope)): 
    6580            log.debug("Request for token of ID: %s - token was not granted scope %s", 
    6681                      token_id, scope) 
    67             return (None, 'invalid_token') 
     82            return (None, 'insufficient_scope') 
    6883        return (token, None) 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/lib/register/authorization_grant.py

    r8030 r8057  
    2424        self.redirect_uri = request.redirect_uri 
    2525        # Allow for authorized scope to be different from requested scope. 
    26         self.scope = (scope if scope is not None else request.scope) 
     26        self.scope_str = (scope if scope is not None else request.scope) 
    2727        self.additional_data = additional_data 
    2828        self.timestamp = datetime.utcnow() 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/lib/register/client_authorization.py

    r7952 r8057  
    88__revision__ = "$Id$" 
    99 
     10import ndg.oauth.server.lib.register.scopeutil as scopeutil 
     11 
    1012class ClientAuthorization(object): 
    1113    """ 
     
    1517        self.user = user 
    1618        self.client_id = client_id 
    17         self.scope = scope 
     19        self.scope = scopeutil.scopeStringToList(scope) 
    1820        self.is_authorized = is_authorized 
    1921 
    2022    def eq_authz_basis(self, other): 
     23        """Determines whether a requested client authorization is equivalent to 
     24        a granted one. 
     25        @type other: ClientAuthorization 
     26        @param other: requested authorization 
     27        @rtype: bool 
     28        @return: True if the user and client ID are the same and if there are no 
     29        requested scopes that are not granted, otherwise False 
     30        """ 
    2131        return ((self.user == other.user) 
    2232                and (self.client_id == other.client_id) 
    23                 and (self.scope == other.scope)) 
     33                and scopeutil.isScopeGranted(self.scope, other.scope)) 
    2434 
    2535    def __repr__(self): 
     
    5666    def __repr__(self): 
    5767        s = [] 
    58         for u, uv in self.register.iteritems(): 
    59             for c, cv in uv.iteritems(): 
     68        for uv in self.register.itervalues(): 
     69            for cv in uv.itervalues(): 
    6070                s.append(cv.__repr__()) 
    6171        return ' '.join(s) 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/lib/render/genshi_renderer.py

    r8030 r8057  
    88__revision__ = "$Id$" 
    99 
    10 from genshi.template import MarkupTemplate 
     10import os 
     11 
     12#from genshi.template import MarkupTemplate 
     13from genshi.template import TemplateLoader 
    1114 
    1215from ndg.oauth.server.lib.render.renderer_interface import RendererInterface 
     
    2427        @return: rendered template 
    2528        """ 
    26         tmpl_file = open(filename) 
    27         tmpl = MarkupTemplate(tmpl_file) 
    28         tmpl_file.close() 
     29        fname = os.path.basename(filename) 
     30        dirname = os.path.dirname(filename) 
     31        loader = TemplateLoader(dirname, auto_reload=True) 
     32        tmpl = loader.load(fname) 
    2933        response = tmpl.generate(c=parameters).render('html') 
    3034        return response 
     35 
     36#    def render(self, filename, parameters): 
     37#        """Render a page from a template. 
     38#        @type filename: basestring 
     39#        @param filename: filename of template 
     40#        @type parameters: dict 
     41#        @param parameters: parameters to substitute into template 
     42#        @rtype: basestring 
     43#        @return: rendered template 
     44#        """ 
     45#        tmpl_file = open(filename) 
     46#        tmpl = MarkupTemplate(tmpl_file) 
     47#        tmpl_file.close() 
     48#        response = tmpl.generate(c=parameters).render('html') 
     49#        return response 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/templates/auth_client_form.html

    r7950 r8057  
    1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    2 <html xmlns="http://www.w3.org/1999/xhtml" 
    3       xmlns:py="http://genshi.edgewall.org/" 
    4       xmlns:xi="http://www.w3.org/2001/XInclude" 
    5       lang="en"> 
    6   <head> 
    7     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 
    8     <title>Login</title> 
    9   </head> 
    10   <body> 
    11     <form method="post" action="${c.submit_url}"> 
    12       <table border="0" style="margin-left: auto; margin-right: auto;"> 
    13         <tr> 
    14           <td align="center" colspan="2"> 
    15             Do you grant permission to client "${c.client_name}"? 
    16           </td> 
    17         </tr> 
    18         <tr> 
    19           <td align="center" colspan="2"> 
    20             &nbsp; 
     1<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
     2<html xmlns:xi="http://www.w3.org/2001/XInclude" 
     3      xmlns="http://www.w3.org/1999/xhtml" 
     4      xmlns:py="http://genshi.edgewall.org/"> 
     5  <div py:def="oauthAuthClient()" id="oauthAuthClient"> 
     6    <form action="$c.submit_url" method="post"> 
     7      <table cellspacing="0" border="0" cellpadding="5" style="align: left"> 
     8        <tr align="left"> 
     9          <td colspan="2"> 
     10            <p>If you press the OK button you are granting permission to client "${c.client_name}" to use your credentials. 
     11            Press the cancel button if you do not want to do that.</p> 
     12            <p>Client details:</p> 
    2113          </td> 
    2214        </tr> 
     
    2921          </td> 
    3022        </tr> 
    31         <tr> 
    32           <td align="center" colspan="2"> 
    33             &nbsp; 
     23        <tr py:if="c.scope"> 
     24          <td> 
     25            Scope in which credentials are to be used: 
     26          </td> 
     27          <td> 
     28            ${c.scope} 
    3429          </td> 
    3530        </tr> 
    3631        <tr> 
    37           <td colspan="2"> 
    38             <table border="0" width="100%"> 
    39               <tr> 
    40                 <td align="center"><input type="submit" value="Yes" name="yes-button"/></td> 
    41                 <td align="center"><input type="submit" value="No" name="no-button"/></td> 
    42               </tr> 
    43             </table> 
     32          <td colspan="2" align="right"> 
     33            <input type="submit" name="submit" value="OK"/> 
     34            <input type="submit" name="cancel" value="Cancel"/> 
     35            <span> 
     36              <a href="javascript:;" title="Toggle help" onclick="toggleDiv(1,'aboutOpenID','shown','hidden','div'); return false;"> 
     37              <img src="$c.helpIcon" alt="Toggle help" class="helpicon"/></a> 
     38            </span> 
    4439          </td> 
    4540        </tr> 
    4641      </table> 
    47     </form>          
     42      <div id="aboutOpenID" class="hidden"> 
     43        <div class="helptxt"> 
     44          <p>You are being asked to log in to an OAuth server. OAuth is a protocol that allows you 
     45          to grant access to services to act on your behalf while only logging in to the OAuth 
     46          server. You will be asked to confirm that you wish to delegate authority to the service. 
     47          </p> 
     48          <p> 
     49          This server uses OAuth version 2.0. The latest specification at the time of writing is 
     50          <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-24">The OAuth 2.0 Authorization Protocol</a> 
     51          </p> 
     52        </div> 
     53      </div> 
     54    </form> 
     55  </div> 
     56 
     57  <xi:include href="base.html" /> 
     58  <head> 
     59    <replace py:replace="pagehead()"/> 
     60  </head> 
     61  <body> 
     62    <div id="main"> 
     63      <div py:replace="header()"/> 
     64      <replace py:replace="oauthAuthClient()"/> 
     65      <div py:replace="footer(showLoginStatus=False)"/> 
     66    </div> 
    4867  </body> 
    4968</html> 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/wsgi/authorization_filter.py

    r8033 r8057  
    1616from ndg.oauth.server.lib.register.client_authorization import ( 
    1717                            ClientAuthorization, ClientAuthorizationRegister) 
     18from ndg.oauth.server.lib.render.configuration import RenderingConfiguration 
    1819from ndg.oauth.server.lib.render.factory import callModuleObject 
    1920from ndg.oauth.server.lib.render.renderer_interface import RendererInterface 
     
    3132    SESSION_CALL_CONTEXT_KEY = 'oauth2_client_authorizations_context' 
    3233    PARAM_PREFIX = 'oauth2authorization.' 
     34    LAYOUT_PREFIX = 'layout.' 
    3335    # Configuration options 
    3436    BASE_URL_PATH_OPTION = 'base_url_path' 
     
    5153        USER_IDENTIFIER_KEY_OPTION: 'REMOTE_USER' 
    5254    } 
     55    LAYOUT_PARAMETERS = ['heading', 
     56                         'title', 
     57                         'message', 
     58                         'leftLogo', 
     59                         'leftAlt', 
     60                         'leftImage', 
     61                         'leftLink', 
     62                         'rightAlt', 
     63                         'rightImage', 
     64                         'rightLink', 
     65                         'footerText', 
     66                         'helpIcon'] 
    5367 
    5468    def __init__(self, app, app_conf, prefix=PARAM_PREFIX, **local_conf): 
     
    7387        """ 
    7488        self._app = app 
     89        self._renderingConfiguration = RenderingConfiguration( 
     90                                                    self.LAYOUT_PARAMETERS, 
     91                                                    prefix + self.LAYOUT_PREFIX, 
     92                                                    local_conf) 
    7593        self._set_configuration(prefix, local_conf) 
    7694        self.client_register = ClientRegister(self.client_register_file) 
     
    217235                 'client_id': client_id, 
    218236                 'scope': scope, 
    219                  'submit_url': submit_url} 
    220             response = self.renderer.render(self.client_authorization_form, c) 
     237                 'submit_url': submit_url, 
     238                 'baseURL': req.application_url} 
     239            response = self.renderer.render(self.client_authorization_form, 
     240                            self._renderingConfiguration.merged_parameters(c)) 
    221241        start_response(self._get_http_status_string(httplib.OK), 
    222242           [('Content-type', 'text/html'), 
     
    249269            return [response] 
    250270 
    251         if ('yes-button' in req.params) and ('no-button' not in req.params): 
     271        if ('submit' in req.params) and ('cancel' not in req.params): 
    252272            log.debug("User authorized client.") 
    253273            granted = True 
  • trunk/ndg_oauth/ndg_oauth_server/ndg/oauth/server/wsgi/oauth2_server.py

    r8033 r8057  
    99 
    1010import httplib 
     11import json 
    1112import logging 
    1213import urllib 
     
    2021from ndg.oauth.server.lib.authenticate.noop_client_authenticator import NoopClientAuthenticator 
    2122from ndg.oauth.server.lib.authorization_server import AuthorizationServer 
    22 from ndg.oauth.server.lib.authorize.authorizer import Authorizer 
    2323from ndg.oauth.server.lib.authorize.authorizer_storing_identifier import AuthorizerStoringIdentifier 
     24from ndg.oauth.server.lib.resource_request.myproxy_cert_request import MyproxyCertRequest 
    2425 
    2526log = logging.getLogger(__name__) 
     
    3738    """ 
    3839    PARAM_PREFIX = 'oauth2server.' 
     40    CERT_DN_ENVIRON_KEY = 'SSL_CLIENT_S_DN' 
    3941    # Configuration options 
    4042    ACCESS_TOKEN_LIFETIME_OPTION = 'access_token_lifetime' 
     
    6870        '/access_token': 'access_token', 
    6971        '/authorize': 'authorize', 
    70         '/check_token': 'check_token' 
     72        '/check_token': 'check_token', 
     73        '/request_certificate': 'request_certificate' 
    7174    } 
    7275 
     
    9699        if self.access_token_type == 'bearer': 
    97100            # Simple bearer token configuration. 
    98             authorizer = Authorizer(self.authorization_grant_lifetime_seconds) 
    99101            access_token_generator = BearerTokenGenerator(self.access_token_lifetime_seconds, self.access_token_type) 
    100102        elif self.access_token_type == 'myproxy': 
    101103            # Configure authorization server to use MyProxy certificates as access tokens. 
    102             authorizer = AuthorizerStoringIdentifier( 
    103                 self.authorization_grant_lifetime_seconds, 
    104                 user_identifier_env_key=self.user_identifier_env_key, 
    105                 user_identifier_grant_data_key=self.USER_IDENTIFIER_GRANT_DATA_KEY) 
    106  
    107104            access_token_generator = MyProxyCertTokenGenerator( 
    108105                self.access_token_lifetime_seconds, self.access_token_type, 
     
    115112                             (self.access_token_type, 
    116113                              self.ACCESS_TOKEN_TYPE_OPTION)) 
     114        # Store user identifier with grant - this isn't needed for the OAuth 
     115        # protocol but is needed to return certificates using MyProxy. 
     116        authorizer = AuthorizerStoringIdentifier( 
     117            self.authorization_grant_lifetime_seconds, 
     118            user_identifier_env_key=self.user_identifier_env_key, 
     119            user_identifier_grant_data_key=self.USER_IDENTIFIER_GRANT_DATA_KEY) 
    117120 
    118121        # Determine client authentication type. A 'none' options is allowed so 
     
    130133            self.client_register_file, authorizer, client_authenticator, 
    131134            access_token_generator, conf) 
     135 
     136        self._myproxy_cert_request = MyproxyCertRequest( 
     137            certificate_request_parameter=self.certificate_request_parameter, 
     138            myproxy_client_env_key=self.myproxy_client_env_key, 
     139            myproxy_global_password=self.myproxy_global_password, 
     140            user_identifier_grant_data_key=self.USER_IDENTIFIER_GRANT_DATA_KEY) 
    132141 
    133142    def __call__(self, environ, start_response): 
     
    177186        @return: WSGI response 
    178187        """ 
     188        log.debug("authorize called") 
     189        # Stop immediately if the client is not registered. 
     190        (error, error_description 
     191                        ) = self._authorizationServer.is_registered_client(req) 
     192        if error: 
     193            log.debug("Error checking if client registered: %s - %s", error, 
     194                      error_description) 
     195            return self._error_response(error, error_description, 
     196                                        start_response) 
     197 
    179198        # User authentication is required before authorization can proceed. 
    180199        user = req.environ.get(self.user_identifier_env_key) 
     
    183202            start_response(self._get_http_status_string(httplib.UNAUTHORIZED), []) 
    184203            return [] 
    185  
    186         # Stop immediately if the client is not registered. 
    187         (error, error_description 
    188                         ) = self._authorizationServer.is_registered_client(req) 
    189         if error: 
    190             return self._error_response(error, error_description, 
    191                                         start_response) 
    192204 
    193205        # User authorization for the client is also required. 
     
    270282        @return: WSGI response 
    271283        """ 
     284        log.debug("access_token called") 
    272285        (response, error_status, error_description) = self._authorizationServer.access_token(req) 
    273         log.debug("Access token response is of type %s", type(response)) 
    274286        if response is None: 
    275287            response = '' 
     
    281293        ] 
    282294        status_str = self._get_http_status_string(error_status if error_status else httplib.OK) 
    283              
     295        if error_status: 
     296            log.debug("Error obtaining access token: %s - %s", status_str, 
     297                      error_description) 
     298 
    284299        start_response(status_str, headers) 
    285300        return [response] 
     
    304319            ('Content-Type', 'application/json; charset=UTF-8'), 
    305320            ('Cache-Control', 'no-store'), 
    306             ('Content-length', str(len(response))) 
     321            ('Content-length', str(len(response))), 
    307322            ('Pragma', 'no-store') 
    308323        ] 
    309324        status_str = self._get_http_status_string(error_status if error_status else httplib.OK) 
    310              
     325 
     326        start_response(status_str, headers) 
     327        return [response] 
     328 
     329    def request_certificate(self, req, start_response): 
     330        """ 
     331        Resource service to issue a certificate based on a certificate request 
     332        contained in the request and the identity of the client for whom the 
     333        access token was issued. 
     334        @type req: webob.Request 
     335        @param req: HTTP request object 
     336 
     337        @type start_response:  
     338        @param start_response: WSGI start response function 
     339 
     340        @rtype: iterable 
     341        @return: WSGI response 
     342        """ 
     343        log.debug("request_certificate called") 
     344        cert = None 
     345        error = None 
     346        status = httplib.OK 
     347        dn = req.environ.get(self.CERT_DN_ENVIRON_KEY) 
     348        if dn: 
     349            log.debug("Found certificate DN: %s", dn) 
     350        else: 
     351            # Client must be authenticated - no other error should be included 
     352            # in this case. 
     353            status = httplib.FORBIDDEN 
     354 
     355        if status == httplib.OK: 
     356            (token, status, 
     357                error) = self._authorizationServer.get_registered_token(req, dn) 
     358 
     359        if status == httplib.OK: 
     360            # Token is valid so get a certificate. 
     361            cert = self._myproxy_cert_request.get_resource(token, req) 
     362            if not cert: 
     363                status = httplib.INTERNAL_SERVER_ERROR 
     364 
     365        content_dict = {'status': status} 
     366        if error: 
     367            content_dict['error'] = error 
     368        if cert: 
     369            content_dict['certificate'] = cert 
     370        response = json.dumps(content_dict) 
     371 
     372        headers = [ 
     373            ('Content-Type', 'application/json; charset=UTF-8'), 
     374            ('Cache-Control', 'no-store'), 
     375            ('Content-length', str(len(response))), 
     376            ('Pragma', 'no-store') 
     377        ] 
     378        status_str = self._get_http_status_string(status if status 
     379                                                  else httplib.OK) 
     380        if error: 
     381            log.debug("Error obtaining certificate: %s - %s", status_str, error) 
     382 
    311383        start_response(status_str, headers) 
    312384        return [response] 
  • trunk/ndg_oauth/ndg_oauth_server/repoze_who.ini

    r7950 r8057  
    22# identificaion and challenge 
    33use = repoze.who.plugins.redirector:make_plugin 
    4 login_url = /login.html 
     4login_url = /authentication/login_form 
     5came_from_param = returnurl 
    56 
    67[plugin:auth_tkt] 
     
    5859# plugin_name;classifier_name:.. or just plugin_name (good for any) 
    5960plugins = 
    60 #      redirector;browser 
     61      redirector;browser 
    6162      basicauth 
    6263 
Note: See TracChangeset for help on using the changeset viewer.