1 | import pylons |
---|
2 | from pylons.templating import Buffet |
---|
3 | from pylons import config, request, session |
---|
4 | import ndg.security.server.sso.sso.lib.helpers as h |
---|
5 | from ndg.security.server.sso.sso.lib.app_globals import Globals |
---|
6 | |
---|
7 | import logging |
---|
8 | log = logging.getLogger(__name__) |
---|
9 | |
---|
10 | log.debug("Defining MyBuffet for OpenID template ...") |
---|
11 | |
---|
12 | class MyBuffet(Buffet): |
---|
13 | def _update_names(self, ns): |
---|
14 | return ns |
---|
15 | |
---|
16 | def_eng = config['buffet.template_engines'][0] |
---|
17 | log.info("def_eng = %s" % def_eng) |
---|
18 | buffet = MyBuffet( |
---|
19 | def_eng['engine'], |
---|
20 | template_root=def_eng['template_root'], |
---|
21 | **def_eng['template_options'] |
---|
22 | ) |
---|
23 | |
---|
24 | for e in config['buffet.template_engines'][1:]: |
---|
25 | buffet.prepare( |
---|
26 | e['engine'], |
---|
27 | template_root=e['template_root'], |
---|
28 | alias=e['alias'], |
---|
29 | **e['template_options'] |
---|
30 | ) |
---|
31 | |
---|
32 | class State: |
---|
33 | pass |
---|
34 | |
---|
35 | |
---|
36 | # State variable for WAYF kid file set-up |
---|
37 | c = State() |
---|
38 | c.openid = 'None' |
---|
39 | c.title = "Where are You From?" |
---|
40 | c.xml = '' |
---|
41 | c.doc = 'logged in' |
---|
42 | c.providers = {} |
---|
43 | |
---|
44 | import base64 |
---|
45 | |
---|
46 | def make_template(): |
---|
47 | '''Make kid template for OpenID login - the NDG WAYF piggy backs this. |
---|
48 | |
---|
49 | It's triggered by a HTTP 401 authorisation error and called explicitly |
---|
50 | via the WAYF controller''' |
---|
51 | |
---|
52 | g = config['pylons.g'] |
---|
53 | |
---|
54 | # Check for return from OpenID login |
---|
55 | try: |
---|
56 | userSet = 'REMOTE_USER' in request.environ |
---|
57 | except TypeError, e: |
---|
58 | # Request object may not be registered - crude fix here wrapping it a |
---|
59 | # catch |
---|
60 | # TODO: referencing environ outside a controller |
---|
61 | log.info("Keying 'REMOTE_USER' in request.environ: %s" % e) |
---|
62 | userSet = False |
---|
63 | |
---|
64 | if userSet: |
---|
65 | if not g.ndg.security.common.sso.state.returnToURL: |
---|
66 | log.error("No returnToURL set for redirect following OpenID " + \ |
---|
67 | "login") |
---|
68 | else: |
---|
69 | log.info("Redirecting to [%s] following OpenID login ..." % \ |
---|
70 | g.ndg.security.common.sso.state.returnToURL) |
---|
71 | h.redirect_to(g.ndg.security.common.sso.state.returnToURL) |
---|
72 | |
---|
73 | state = g.ndg.security.common.sso.state |
---|
74 | cfg = g.ndg.security.common.sso.cfg |
---|
75 | |
---|
76 | # Set encoded return to address - ensure login can return to an address |
---|
77 | # over https to preserve confidentiality of credentials |
---|
78 | # TODO: revisit - at the moment a redirect back from https -> http at the |
---|
79 | # client to the IdP is rejected |
---|
80 | # if state.returnToURL and cfg.server in state.returnToURL: |
---|
81 | # state.returnToURL = state.returnToURL.replace(cfg.server, |
---|
82 | # cfg.sslServer) |
---|
83 | # log.debug("make_template: switched return to address to https = %s" % \ |
---|
84 | # state.returnToURL) |
---|
85 | |
---|
86 | state.b64encReturnToURL = base64.urlsafe_b64encode(str(state.returnToURL)) |
---|
87 | |
---|
88 | # Retrieve IdP details |
---|
89 | _getTrustedIdPs(g) |
---|
90 | |
---|
91 | return _render("ndg.security.wayf", h=h, g=g, c=c) |
---|
92 | |
---|
93 | |
---|
94 | def _render(templateName, **kw): |
---|
95 | '''TODO: Wrapper to enable substitution of $message and $css_class used by |
---|
96 | AuthKit open_id module''' |
---|
97 | rendering = buffet.render('ndg.security.kid', |
---|
98 | template_name=templateName, |
---|
99 | namespace=kw) |
---|
100 | # Add $message and $css_class here somehow |
---|
101 | return rendering |
---|
102 | |
---|
103 | |
---|
104 | from ndg.security.server.wsgi.utils.attributeauthorityclient import \ |
---|
105 | WSGIAttributeAuthorityClient |
---|
106 | |
---|
107 | def _getTrustedIdPs(g): |
---|
108 | '''Retrieve list of trusted login sites for user to select - calls |
---|
109 | Attribute Authority WS''' |
---|
110 | |
---|
111 | # Get references to globals |
---|
112 | state = g.ndg.security.common.sso.state |
---|
113 | cfg = g.ndg.security.server.sso.cfg |
---|
114 | |
---|
115 | # Check for cached copy and return if set to avoid recalling |
---|
116 | # Attribute Authority - This has the consequence that if the list |
---|
117 | # of trusted hosts in the Map Configuration changes, the Attribute |
---|
118 | # Authority and THIS service must be restarted. |
---|
119 | if len(g.ndg.security.server.sso.state.trustedIdPs) > 0: |
---|
120 | return _render('ndg.security.wayf', h=h, g=g, c=c) |
---|
121 | |
---|
122 | log.debug("Initialising connection to Attribute Authority [%s]" % \ |
---|
123 | cfg.aaURI) |
---|
124 | |
---|
125 | try: |
---|
126 | aaClnt = WSGIAttributeAuthorityClient( |
---|
127 | environ=pylons.request.environ, |
---|
128 | uri=cfg.aaURI, |
---|
129 | environKey=self.cfg.aaEnvironKey, |
---|
130 | tracefile=cfg.tracefile, |
---|
131 | httpProxyHost=cfg.httpProxyHost, |
---|
132 | noHttpProxyList=cfg.noHttpProxyList, |
---|
133 | **cfg.wss) |
---|
134 | except Exception, e: |
---|
135 | c.xml='Error establishing security context. Please report ' + \ |
---|
136 | 'the error to your site administrator' |
---|
137 | log.error("Initialising AttributeAuthorityClient for " + \ |
---|
138 | "getAllHostsInfo call: %s" % e) |
---|
139 | return _render('ndg.security.error', h=h, g=config['pylons.g'], c=c) |
---|
140 | |
---|
141 | # Get list of login uris for trusted sites including THIS one |
---|
142 | log.debug("Calling Attribute Authority getAllHostsInfo for wayf ...") |
---|
143 | |
---|
144 | try: |
---|
145 | hosts = aaClnt.getAllHostsInfo() |
---|
146 | except Exception, e: |
---|
147 | c.xml='Error getting a list of trusted sites for login. ' + \ |
---|
148 | 'Please report the error to your site administrator.' |
---|
149 | log.error("AttributeAuthorityClient getAllHostsInfo call: %s" % e) |
---|
150 | return _render('ndg.security.error', h=h, g=config['pylons.g'], c=c) |
---|
151 | |
---|
152 | g.ndg.security.server.sso.state.trustedIdPs = \ |
---|
153 | dict([(k, v['loginURI']) for k, v in hosts.items()]) |
---|
154 | |
---|
155 | |
---|
156 | from ndg.security.common.pylons.security_util import setSecuritySession |
---|
157 | from urlparse import urlsplit |
---|
158 | |
---|
159 | def url2user(environ, url): |
---|
160 | '''Function picked up by authkit.openid.urltouser config setting. It |
---|
161 | sets a username from the users OpenID URL following login''' |
---|
162 | log.info("OpenID sign in with [%s]" % url) |
---|
163 | |
---|
164 | # Remove protocol prefix and strip /'s |
---|
165 | username = ''.join(urlsplit(url)[1:]).strip('/').replace('/', '-') |
---|
166 | return username |
---|