Changeset 1123 for trunk/rp/trac

Show
Ignore:
Timestamp:
12/07/07 13:26:22 (3 years ago)
Author:
dbuss
Message:

added checks for audience, missing required claims, extra claims, and other IIW RP compliance checks. Added ability to upgrade/downgrade any message severity generated while processing an infocard. Added functionality to allow more than one object tag to pre present on the login page with each a specifice set of configuration options. Added default user. Spiffied up some documentation and comments, while I was there removed some cruft.

Location:
trunk/rp/trac/infocard_acct/0.11/infocard_acct
Files:
1 added
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/db.py

    r1102 r1123  
    5353                db = self.env.get_db_cnx() 
    5454                cursor = db.cursor() 
    55                 self.log.debug('DBAssociationStore:set_association for \"%s\" to \"%s\"', user, cardkeyhash) 
     55#               self.log.debug('DBAssociationStore:set_association for \"%s\" to \"%s\"', user, cardkeyhash) 
    5656                cursor.execute("INSERT INTO cardkey " 
    5757                        "(username,cardkeyhash) " 
     
    6767                db = self.env.get_db_cnx() 
    6868                cursor = db.cursor() 
    69                 self.log.debug('DBAssociationStore:check_association for \"%s\"', cardkeyhash) 
     69#               self.log.debug('DBAssociationStore:check_association for \"%s\"', cardkeyhash) 
    7070                cursor.execute("SELECT username FROM cardkey " 
    7171                                           "WHERE cardkeyhash=%s", (cardkeyhash,)) 
    7272                user = None 
    7373                for row in cursor: 
    74                         self.log.debug('DBAssociationStore:check_association returning \"%s\"', row[0]) 
     74#                       self.log.debug('DBAssociationStore:check_association returning \"%s\"', row[0]) 
    7575                        return row[0] 
    7676                return None 
     
    193193                        self.found_db_version = int(value[0]) 
    194194 
    195                 self.log.debug('InfoCardAccountEnv: Current db version %s, required is %s' \ 
    196                                 % (self.found_db_version, db_default.db_version)) 
     195#               self.log.debug('InfoCardAccountEnv: Current db version %s, required is %s' \ 
     196#                               % (self.found_db_version, db_default.db_version)) 
    197197 
    198198                if self.found_db_version == db_default.db_version: 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/groups.py

    r1103 r1123  
    2525from session import SecTokenChangeListener 
    2626 
     27from infocard import infocardlib 
     28 
    2729session_var_name = 'sec_groups' 
    2830 
    2931class SecTokenGroups(Component): 
    3032        """Using Policy to move items from the security token to the session as groups 
    31         evaluated once, then cleared from the cache on logout""" 
     33        evaluated once, then cleared from the cache on logout, 
     34        Also provides permission groups by evaluating what is stored on the session 
     35        """ 
    3236         
    33         implements(SecTokenChangeListener) 
     37        implements(SecTokenChangeListener, IPermissionGroupProvider) 
    3438 
    3539        def __init__(self): 
     
    3943                        self.env.log.debug('SecTokenGroups disabled') 
    4044                        return 
    41                 gDefSets =  self.config.getlist('infocard_acct', 'group_definitions') 
    42                 for gDef in gDefSets: 
    43                         gDef = gDef.strip()  #remove any unsightly spaces 
     45 
    4446#                       self.env.log.debug('SecTokenGroups processing group definition : ' + gDef) 
    4547                        groupRules = {}                  
    4648                        groupRules['name'] = gDef 
    4749                        groupRules['exclusive'] = self.config.getbool(gDef, 'exclusive', False) 
    48                         groupRules['matchEval'] = self.config.get(gDef, 'match_eval', '1') 
    49                         groupRules['ruleEval'] = self.config.get(gDef, 'rule_eval') 
    50                         if gDef and groupRules.has_key('ruleEval'): 
     50                        groupRules['matchExpression'] = self.config.get(gDef, 'match_expresion') 
     51                        groupRules['matchStatement'] = self.config.get(gDef, 'match_statement') 
     52                        groupRules['ruleExpresion'] = self.config.get(gDef, 'rule_expresion') 
     53                        groupRules['ruleStatement'] = self.config.get(gDef, 'rule_statement') 
     54                         
     55                        if gDef and (groupRules.has_key('ruleExpresion')  
     56                                or groupRules.has_key('ruleStatment')): 
    5157                                self.gDefs[gDef] = groupRules 
    5258 
     
    6268                        rule = self.gDefs[ruleName] 
    6369                        try: 
    64                                 if self._does_rule_match(rule, session): 
    65                                         values = self._evalute_rule(rule, session) 
     70                                if self._does_rule_match(rule, secToken): 
     71                                        values = self._evalute_rule(rule, secToken) 
    6672                                        if values: 
    6773                                                self.env.log.debug('SecTokenGroups rule adds : '\ 
     
    7480                                self.env.log.debug('SecTokenGroups error evaluating rule: %s' % (ruleName))  
    7581                         
    76                 if groups: 
    77                         self.env.log.debug('SecTokenGroups groups: ' + ','.join(groups)) 
     82                if groups and groups.count(): 
     83                        self.env.log.debug('SecTokenGroups groups (%d): ' %(groups.count() ) + ','.join(groups)) 
    7884 
    7985                return groups 
     
    8490                        del req.session[session_var_name] 
    8591 
    86         def _does_rule_match(self, rule, session): 
    87                 try: 
    88                         if rule.has_key('matchEval'): 
    89                                 eval(rule['matchEval'], None, session) 
    90                         else: 
    91                                 return True 
    92                 except Exception: 
    93                         return False 
    94          
    95         def _evalute_rule(self, rule, session): 
    96                 if rule and session: 
    97                         try: 
    98                                 if rule.has_key('ruleEval'): 
    99                                         data = eval(rule['ruleEval'], None, session) 
    100                                         return (data,) 
     92        def _does_rule_match(self, rule, secToken): 
     93#               try: 
     94                locals = {'secToken': sectoken} 
     95                if rule.has_key('matchExpresion'): 
     96                        eval(rule['matchExpresion'], None, locals) 
     97                else: 
     98                        return True 
     99#               except Exception: 
     100#                       return False 
     101 
     102        def _evalute_rule(self, rule, secToken): 
     103                if rule and secToken: 
     104#                       try: 
     105                        locals = {'secToken': sectoken} 
     106                        if rule.has_key('ruleExpresion'): 
     107                                data = eval(rule['ruleExpresion'], None, locals) 
     108                                return (data,) 
     109                        elif rule.has_key('ruleStatement'): 
     110                                pass 
    101111#                               else: 
    102112#                                       data = eval('tok_claim_privatepersonalidentifier', None, session) 
    103113#                                       return (data,) 
    104                         except Exception: 
    105                                 pass 
     114#                       except Exception: 
     115#                               pass 
    106116                return None 
    107  
    108  
    109 class SessionGroupProvider(Component): 
    110         """ 
    111         Provides permission groups by evaluating what is stored on the session 
    112         Note, this doesn't change from invocation to invocation so often a cache 
    113         may be used. 
    114         """ 
    115         implements(IPermissionGroupProvider) 
    116117 
    117118        # IPermissionGroupProvider interface 
     
    127128                                "WHERE sid=%s and authenticated=%s and name=%s", (username, int(True), session_var_name)) 
    128129                        for name, value in cursor: 
    129                                 self.env.log.debug('SessionGroupProvider groups: ' + ','.join(value)) 
    130                                 return value.split(' ') 
     130                                if value and len(value): 
     131                                        self.env.log.debug('SecTokenGroups groups: ' + ','.join(value)) 
     132                                        return value.split(' ') 
    131133                except Exception: 
    132134                        pass 
    133                          
     135 
    134136                return [] 
    135137 
     138 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/session.py

    r1103 r1123  
    6363        def login(self, req, secToken): 
    6464                """ on login set the email address and name on the session""" 
     65                 
     66#               self.log.debug('SecTokenPreferences setup %s %s' % (req, req.session)) 
    6567                if secToken and req.session: 
    6668                        email = secToken.getAssertion('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress') 
     
    7375                                fullname += ' ' + last 
    7476 
    75                         self.log.debug('SecTokenPreferences login  %s, %s' % (email, fullname)) 
    76                          
     77#                       self.log.debug('SecTokenPreferences login:  %s, %s' % (email, fullname)) 
     78 
    7779                        #visit should we override or update the email or just set if not set? 
    7880                        if email and (self.overrideSession or not req.session.has_key('email')): 
    7981                                req.session['email'] = email 
    80                         if fullname and (self.overrideSession or not req.session.has_ye('name')): 
     82                        if fullname and (self.overrideSession or not req.session.has_key('name')): 
    8183                                req.session['name'] = fullname 
    8284 
     
    113115                """On logout we should cleanup all of the garbage so that subsequent  
    114116                sessions don't end up with incorrect data""" 
    115 #               meta = req.session.get('tok_meta') 
     117                meta = req.session.get('tok_meta') 
    116118#               if meta: 
    117119#                       for key in meta.rsplit(' '): 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/templates/authenticate.html

    r1102 r1123  
    5555      </form> 
    5656 
    57  
    58           <div py:if="display_infocard" > 
    59                   <h1>Login using InfoCard</h1> 
    60          
    61                   <div class="system-message" py:if="infocard_error"> 
    62                         <h2>Error</h2> 
    63                         <p>${infocard_error}</p> 
    64                   </div> 
    65          
    66                   <form name="infocardLogin" id="infocardLogin" method="post" action=""> 
    67                         <img style="display: block" 
    68                         width="92" height="64" 
    69                         src="${href.chrome('/infocard_acct/images/infocard_92x64.png')}" 
    70                                 alt="InfoCard Login"   
    71                         onClick='image_disable(infocardLogin)'/> 
    72                         <input type="hidden" name="referer" value="${referer}" /> 
    73                         <input py:if="blockID" type="hidden" name="blockID" value="${blockID}" /> 
    74                         <OBJECT type="application/x-informationCard" name="xmlToken"> 
    75                            <PARAM Name="tokenType" Value="urn:oasis:names:tc:SAML:1.0:assertion"/> 
    76                            <!--PARAM Name="tokenType" Value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"/--> 
    77                            <PARAM py:if="required_claims" Name="requiredClaims"  
    78                                         Value="${required_claims}"> 
    79                            </PARAM> 
    80                            <PARAM py:if="optional_claims" Name="optionalClaims"  
    81                                         Value="${optional_claims}"> 
    82                            </PARAM> 
    83                            <PARAM py:if="issuer" Name="issuer"  
    84                                         Value="${issuer}"> 
    85                            </PARAM> 
    86                            <PARAM py:if="issuerPolicy" Name="issuerPolicy"  
    87                                         Value="${issuerPolicy}"> 
    88                            </PARAM> 
    89                            <PARAM py:if="privacy_policy" Name="privacyUrl"  
    90                                         Value="${privacy_policy}"> 
    91                            </PARAM> 
    92                            <PARAM py:if="privacy_version" Name="privacyVersion"  
    93                                         Value="${privacy_version}"> 
    94                            </PARAM> 
    95                         </OBJECT> 
    96                   </form> 
    97                 </div> 
    98     </div> 
    99          
     57      <div py:for="name in infocards.keys()"> 
     58                <div py:if="display_infocard"  py:with="element = infocards[name]" > 
     59                  <fieldset id="${element.blockID}"> 
     60                          <h1 py:if="element.header_text">${element.header_text}</h1> 
     61                          <p class="help" py:if="element.help_text"> 
     62                                ${element.help_text} 
     63                          </p> 
     64                          <div class="system-message" py:if="infocard_error"> 
     65                                <h2>Error</h2> 
     66                                <p>${infocard_error}</p> 
     67                          </div> 
     68                 
     69                          <form name="${element.blockID}" id="${element.blockID}" method="post" action=""> 
     70                                <img style="display: block" 
     71                                   width="71" height="50" 
     72                                   src="${href.chrome('/infocard_acct/images/infocard_71x50.png')}" 
     73                                   alt="InfoCard Login"   
     74                                   onClick='${element.blockID}.submit()'/> 
     75                                <input type="hidden" name="referer" value="${referer}" /> 
     76                                <input type="hidden" name="blockID" value="${element.blockID}" /> 
     77                                <OBJECT type="application/x-informationCard" name="xmlToken"> 
     78                                   <PARAM Name="tokenType" Value="${element.token_type}"/> 
     79                                   <PARAM py:if="element.required_claims" Name="requiredClaims"  
     80                                                Value="${element.required_claims}"/> 
     81                                   <PARAM py:if="element.optional_claims" Name="optionalClaims"  
     82                                                Value="${element.optional_claims}" /> 
     83                                   <PARAM py:if="element.issuer" Name="issuer"  
     84                                                Value="${element.issuer}" /> 
     85                                   <PARAM py:if="element.issuerPolicy" Name="issuerPolicy"  
     86                                                Value="${element.issuerPolicy}" /> 
     87                                   <PARAM py:if="element.privacy_policy" Name="privacyUrl"  
     88                                                Value="${element.privacy_policy}" /> 
     89                                   <PARAM py:if="element.privacy_version" Name="privacyVersion"  
     90                                                Value="${element.privacy_version}" /> 
     91                                </OBJECT> 
     92                          </form> 
     93                        </fieldset> 
     94                 </div> 
     95           </div> 
     96     </div> 
    10097  </body> 
    10198</html> 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/templates/infocard-detail.html

    r1098 r1123  
    88  <head> 
    99    <title>${title}</title> 
    10     <script type="text/javascript"> 
    11     $(document).ready(function() { 
    12         $('#user')[0].focus(); 
    13     }); 
    14     </script> 
    1510  </head> 
    1611 
     
    2722                <thead> 
    2823          <tr> 
    29             <th>label</th><th>description</th><th>severity</th> 
     24            <th>severity</th><th>label</th><th>description</th> 
    3025          </tr> 
    3126        </thead> 
    3227        <tbody> 
     28                <?python 
     29                   import logging 
     30                   def severityName(level): 
     31                      return logging.getLevelName(level) 
     32                ?> 
    3333                 <div py:for="element in events"> 
    3434                        <tr> 
    35                                 <td><pre>${element.severity}</pre></td> 
     35                                <td><pre>${severityName(element.severity)}</pre></td> 
    3636                                <td><pre>${element.tag}</pre></td> 
    3737                                <td><pre>${element.text}</pre></td> 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/templates/infocard-session-detail.html

    r1103 r1123  
    88  <head> 
    99    <title>${title}</title> 
    10     <script type="text/javascript"> 
    11     $(document).ready(function() { 
    12         $('#user')[0].focus(); 
    13     }); 
    14     </script> 
    1510  </head> 
    1611 
  • trunk/rp/trac/infocard_acct/0.11/infocard_acct/web_ui.py

    r1103 r1123  
    3535import re 
    3636import time 
     37import urlparse 
    3738 
    3839from trac import perm, util 
     
    5556 
    5657import infocard.xmlseclibs, infocard.infocardlib 
    57 from infocard import infocardlib 
     58from infocard import infocardlib, event 
    5859from infocard.infocardlib import SecToken, InfoCardProcessor 
    5960 
     
    7172        privateKey = '/' 
    7273        privateKeyPassPhrase = '' 
    73         processors = {} 
    7474        mandatoryClaims = ('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier',) 
    75         displayDebug = False 
    7675 
    7776        def __init__(self): 
    78                 privateKey = PathOption('infocard_acct', 'private_key_path', '/') 
    79                 privateKeyPassPhrase = PathOption('infocard_acct', 'private_key_pass_phrase', '') 
    80                 requiredClaims =  Option('infocard_acct', 'required_claims', '') 
    81                 optionalClaims = Option('infocard_acct', 'optional_claims', '') 
    82                 privacyPolicy = Option('infocard_acct', 'privacy_url', '') 
    83                 privacyPolicyVer = Option('infocard_acct', 'privacy_version', '') 
    84                 issuer = Option('infocard_acct', 'privacy_issuer', '') 
    85  
    86                 self.privateKey = self.env.config['infocard_acct'].get('private_key_path') 
    87                 self.requiredClaims = self.env.config['infocard_acct'].get('required_claims') 
    88                 self.optionalClaims = self.env.config['infocard_acct'].get('optional_claims') 
    89                 self.privacyPolicy = self.env.config['infocard_acct'].get('privacy_url') 
    90                 self.privacyPolicyVer = self.env.config['infocard_acct'].get('privacy_version') 
    91                 self.issuer = self.env.config['infocard_acct'].get('issuer') 
    92                 self.displayDebug = self.config.getbool('infocard_acct', 'debug', False) 
     77                self.processors = {} 
     78 
     79                self.privateKey = self.config.get('infocard_acct', 'private_key_path') 
     80                self.privateKeyPassPhrase = self.config.get('infocard_acct', 'private_key_pass_phrase') 
     81                self.processors['infocard_acct'] = self._process_options('infocard_acct') 
     82                 
     83                #read any sets of infocard options 
     84                Sets =  self.config.getlist('infocard_acct', 'infocard_definitions') 
     85                if Sets: 
     86                        for element in Sets: 
     87                                name = element.strip()  #remove any unsightly spaces 
     88                                self.processors[name] = self._process_options(name) 
     89                 
     90                scheme, host = urlparse.urlparse(self.env.abs_href())[:2] 
     91                if not scheme or not host: 
     92                        self.log.error('Trac configuration for \'base_url\' option '\ 
     93                        'doesn\'t contain host and scheme information.') 
     94                         
     95        def _process_options(self, tag): 
     96                """read a block of options from the configuration and store them in a  
     97                local structure for later use. 
     98                """ 
     99                options = {} 
     100                #uniquely name this block of options using it's tag from trac.ini 
     101                options['blockID'] = tag 
     102 
     103                #process basic options with no defaults 
     104                config_fields = ['required_claims', 'optional_claims',  
     105                        'privacy_url', 'privacy_version', 'issuer', 'associated_user',  
     106                        'Audience'] 
     107                for field in config_fields: 
     108                        data =  self.config.get(tag, field) 
     109                        if data: 
     110                                options[field] = data 
     111 
     112                #process warning levels and debug options, these may result in upgrades 
     113                #or downgrades in error reporting 
     114                for field in event.config_fields: 
     115                        data = self.config.get(tag, field) 
     116                        if data: 
     117                                options[field] = data 
     118 
     119                #process options which have defaults and need to be  
     120                options['debug_page'] = self.config.getbool(tag, 'debug_page', False) 
     121                options['header_text'] = self.config.get(tag, 'header_text', 'InfoCard Login') 
     122                options['help_text'] = self.config.get(tag, 'help_text', 'Use any InfoCard to login to your existing account') 
     123                options['token_type'] = self.config.get(tag, 'token_type', 'urn:oasis:names:tc:SAML:1.0:assertion') 
    93124 
    94125                #append all hard coded mandatory claims to the required claims  
    95                 rClaims = None 
    96                 if self.requiredClaims: 
    97                         rClaims = self.requiredClaims.rsplit(' ') 
    98                 for claim in self.mandatoryClaims: 
    99                         if not rClaims or not (claim in rClaims):   
    100                                 self.requiredClaims = self.requiredClaims + ' ' + claim  
    101  
     126                if options.has_key('required_claims'): 
     127                        options['required_claims'] = options['required_claims'] + " " + " ".join(self.mandatoryClaims) 
     128                else: 
     129                        options['required_claims'] = " " + ' '.join(self.mandatoryClaims) 
     130 
     131                #setup basic infocard processor for duty 
     132                processor = InfoCardProcessor() 
     133                if processor: 
     134                        processor.set_decode(self.privateKey, self.privateKeyPassPhrase, True, False) 
     135                        processor.set_claims( 
     136                                self._get_option_from_options(options, 'required_claims'),  
     137                                self._get_option_from_options(options, 'optional_claims')) 
     138                        processor.set_options(options) 
     139                        options['processorTag'] = processor 
     140#               self.log.debug("For %s required: %s, optional: %s" %  
     141#                       (tag, self._get_option_from_options(options, 'required_claims'),  
     142#                               self._get_option_from_options(options, 'optional_claims'))) 
     143                return options 
     144 
     145        def _was_sent_secure(self, req): 
     146#               self.log.error('was_sent_secure: '+req.abs_href.base) 
     147#               host = req.get_header('Host') 
     148#               if not host: 
     149#                       self.log.error('Request missing Host header, assuming secure') 
     150#                       return True 
     151#               scheme, host = urlparse.urlparse(host)[:2] 
     152#               if scheme == 'https': 
     153                        return True 
     154#               return False 
     155 
     156        def _redirect_secure(self, req): 
     157                """ redirect to absolute url over https for /login """  
     158                url = urlparse.urlparse(self.env.abs_href()) 
     159                if not url.hostname: 
     160                        host = req.get_header('Host') 
     161                else: 
     162                        host = url.hostname 
     163                req.redirect(urlparse.urlunparse(('https', host,  
     164                        url.path, req.path_info, None, None))) 
    102165 
    103166        def authenticate(self, req): 
     167                """called when req.authname is accessed, since this is actually called  
     168                by many processes in trac, it will often be the time when the security 
     169                token is read and processed.  """ 
    104170                #self.log.debug('web_ui:LoginModule:authenticate' ) 
    105171                authnanme = None 
     
    117183 
    118184        def match_request(self, req): 
     185                """See if we handle the request, we watch for login/logout""" 
    119186                if if_enabled(auth.LoginModule.match_request) \ 
    120187                        and ( (re.match(r'/login/?$', req.path_info) is not None) \ 
     
    125192 
    126193        def process_request(self, req): 
    127 #               self.log.debug('web_ui:LoginModule:process_request : '+req.path_info) 
     194                """ handle display of the login page, display of login results,  
     195                and logout requests""" 
     196 
     197#               self.log.debug('web_ui:LoginModule:process_request : %s' %  
     198#                       (req.path_info)) 
    128199                if req.path_info.startswith('/logout'): 
    129200                        self._cleanup_session(req) 
    130201                if req.path_info.startswith('/login'): 
    131202                        if req.authname == 'anonymous': 
     203                                 
     204                                if not self._was_sent_secure(req): 
     205                                        self._redirect_secure(req) 
     206                                 
    132207                                data = { 
    133208                                        'title': 'Login', 
     
    136211                                        'referer': self._referer(req), 
    137212                                        'reset_password_enabled': AccountModule(self.env).reset_password_enabled, 
    138                                         'required_claims': self.requiredClaims, 
    139                                         'optional_claims': self.optionalClaims, 
    140                                         'privacy_policy': self.privacyPolicy, 
    141                                         'privacy_version': self.privacyPolicyVer, 
    142                                         'issuer': self.issuer, 
    143                                         'submit_text': 'Login' 
     213                                        'submit_text': 'Login', 
     214                                        'display_infocard': True, 
     215                                        'infocards': self.processors 
    144216                                } 
    145  
     217                                 
    146218                                if not req.args.get('xmlToken') and not req.args.get('cardkeyhash'): 
    147                                         data['display_infocard'] = 'True' 
     219                                        data['display_infocard'] = True 
    148220                                        if req.method == 'POST': 
    149221                                                data['login_error'] = 'Invalid username or password' 
     
    160232                                                        #invalid security token, handle error 
    161233                                                        data['infocard'] = secToken 
     234                                                        data['title'] = 'Invalid Infocard Detail' 
    162235                                                        if secToken.eventLog: 
    163236                                                                data['events'] = secToken.eventLog.events 
     
    171244                                        data['login_error'] = 'Credentials have not been associated with your account' 
    172245                                        data['submit_text'] = 'Associate' 
    173                                         data['blockID'] = 'default' 
     246                                        data['blockID'] = 'infocard_acct' 
    174247                                return 'authenticate.html', data, None 
    175                         elif req.args.get('xmlToken') and self.displayDebug: 
    176                                 self._do_debug_login(req) 
    177                                 self._setup_session(req) 
     248                        elif req.args.get('xmlToken'): 
    178249                                secToken = self._get_token(req) 
    179                                 data = { 
    180                                         'title': 'Infocard Debug', 
    181                                         'infocard': secToken 
    182                                 } 
     250                                data = {'infocard': secToken} 
     251                                 
     252                                if self._get_option(req, 'debug_page'): 
     253                                        self._do_debug_login(req) 
     254                                        self._setup_session(req) 
     255                                        data['title'] = 'Infocard Debug' 
     256                                if not secToken.isValid: 
     257                                        data['title'] = 'Invalid Infocard Detail' 
    183258                                if secToken.eventLog: 
    184259                                        data['events'] = secToken.eventLog.events 
    185                                 return 'infocard-detail.html', data, None 
    186  
    187                         self.log.debug("%s",  self.displayDebug) 
     260                                if (not secToken.isValid) or self._get_option(req, 'debug_page'): 
     261                                        return 'infocard-detail.html', data, None 
     262 
    188263                        self._setup_session(req) 
    189264                return auth.LoginModule.process_request(self, req) 
    190265 
    191266        def _cleanup_session(self, req): 
    192                 """We need to delete session attributes here 
     267                """We need to delete session attributes here, allow all people who  
     268                potentially cached information from the login to act on the logout 
    193269                """ 
    194270                SecTokenSessionModule(self.env).logout(req) 
     
    204280                It should be only used for the debug login. 
    205281                """ 
     282 
    206283                ignore_case = BoolOption('trac', 'ignore_auth_case', 'false', 
    207284                """Whether case should be ignored for login names (''since 0.9'').""") 
     
    221298                req.outcookie['trac_auth'] = cookie 
    222299                req.outcookie['trac_auth']['path'] = req.href() 
    223                  
    224         def _get_processor(self, req): 
    225                 """Manage the singletons for proecessors   
    226                 """ 
    227                 processor = None 
    228                 processorTag = req.args.get('blockID') 
    229                 if not processorTag: 
    230                         processorTag = 'default' 
     300 
     301        def _get_option_from_options(self, options, tag): 
     302                if options and options.has_key(tag): 
     303                        return options[tag] 
     304                return None 
     305 
     306        def _get_option(self, req, tag): 
     307                """Manage the options lists for all infocard login configs   
     308                """ 
     309                block = req.args.get('blockID') 
     310                if not block: 
     311                        block = 'infocard_acct' 
    231312                try: 
    232                         processor = self.processors[processorTag] 
     313                        options = self.processors[block] 
     314                        if options and tag == 'processorTag' and not options.has_key('Audience'): 
     315                                #I really hate doing this here, but it's the only place we  
     316                                #have all of the required information 
     317                                url = urlparse.urlparse(self.env.abs_href()) 
     318                                if not url.hostname: 
     319                                        host = req.get_header('Host') 
     320                                else: 
     321                                        host = url.hostname 
     322                                Audience = (urlparse.urlunparse(('https', host,  
     323                                                                req.base_path, None, None, None)) 
     324                                                        +req.path_info) 
     325                                options['Audience'] = Audience 
     326                                processor = self._get_option_from_options(options, tag) 
     327                                if processor: 
     328                                        processor.set_options({'Audience': Audience}) 
     329                        return self._get_option_from_options(options, tag) 
    233330                except (NameError, KeyError): 
    234                         processor = InfoCardProcessor() 
    235                         if processor: 
    236                                 processor.set_decode(self.privateKey, self.privateKeyPassPhrase, True, False) 
    237                                 processor.set_claims(self.requiredClaims, self.optionalClaims) 
    238                                 self.processors[processorTag] = processor 
    239                                  
    240                 return processor 
     331                        pass 
    241332 
    242333 
     
    249340                        secToken = req.environ['Security_Token'] 
    250341                except (NameError, KeyError): 
    251                         processor = self._get_processor(req) 
     342                        processor = self._get_option(req, 'processorTag') 
    252343                        secToken = processor.process_token(req.args.get('xmlToken')) 
    253344                        req.environ['Security_Token'] = secToken 
    254345                return secToken 
    255346 
    256         def _do_login(self, req): 
    257                 if not req.remote_user:                                       
    258                         req.redirect(self.env.abs_href()) 
    259                 return auth.LoginModule._do_login(self, req) 
    260  
    261347        def _remote_user(self, req): 
     348                """ Called to determine the name of the user, it's poorly named.  It has 
     349                the side effect of actually verifying username/password or credentials 
     350                """ 
     351                 
    262352#               self.log.debug('web_ui:LoginModule:_remote_user' ) 
    263353                user = req.args.get('user') 
     
    287377                                                return user 
    288378                                        else: 
     379                                                associateduser = self._get_option(req, 'associated_user') 
     380                                                if associateduser: 
     381                                                        return associateduser 
    289382                                                self.log.debug('web_user:LoginModule:_remote_user:check_association failed') 
    290383                #self.log.debug('web_user:LoginModule:_remote_user: -> None') 
     
    292385 
    293386        def _redirect_back(self, req):                                                                                                                   
    294                 """Redirect the user back to the URL she came from.""" 
     387                """Redirect the user back to the URL she came from, only used during  
     388                debug logins, otherwise we allow trac to handle the request redirect.""" 
    295389                referer = self._referer(req) 
    296390                if referer and not referer.startswith(req.base_url): 
     
    315409                """ 
    316410                from pkg_resources import resource_filename 
    317 #               self.log.debug('LoginModule:get_htdocs_dirs: %s', resource_filename(__name__, 'htdocs')) 
    318411                return [('infocard_acct', resource_filename(__name__, 'htdocs')), 
    319412                   ('site', self.env.get_htdocs_dir())] 
     
    324417                """ 
    325418                from pkg_resources import resource_filename 
    326 #               self.log.debug('LoginModule:get_templates_dirs: %s', resource_filename(__name__, 'templates')) 
    327419                return [resource_filename(__name__, 'templates')] 
    328420