@@ -78,6 +78,36 @@ def delete_session(self, ticket):
7878 logging .debug ('[CAS] Deleting session for ticket {}' .format (ticket ))
7979 self .session_storage_adapter .delete (ticket )
8080
81+ def get_api_url (
82+ self ,
83+ api_resource ,
84+ auth_token_ticket ,
85+ authenticator ,
86+ private_key ,
87+ service_url = None ,
88+ ** kwargs
89+ ):
90+ '''
91+ Build an auth-token-protected CAS API url.
92+ '''
93+ auth_token , auth_token_signature = self ._build_auth_token_data (
94+ auth_token_ticket ,
95+ authenticator ,
96+ private_key ,
97+ ** kwargs
98+ )
99+ params = {
100+ 'at' : auth_token ,
101+ 'ats' : auth_token_signature ,
102+ }
103+ if service_url is not None :
104+ params ['service' ] = service_url
105+ url = '{}?{}' .format (
106+ self ._get_api_url (api_resource ),
107+ urlencode (params ),
108+ )
109+ return url
110+
81111 def get_auth_token_login_url (
82112 self ,
83113 auth_token_ticket ,
@@ -91,26 +121,13 @@ def get_auth_token_login_url(
91121
92122 See https://github.com/rbCAS/CASino/wiki/Auth-Token-Login for details.
93123 '''
94- rsa_key = RSA .importKey (private_key )
95- signer = PKCS1_v1_5 .new (rsa_key )
96-
97- auth_token = json .dumps ({
98- 'authenticator' : authenticator ,
99- 'username' : username ,
100- 'ticket' : auth_token_ticket ,
101- })
102- if six .PY3 :
103- auth_token = auth_token .encode ('utf-8' )
104-
124+ auth_token , auth_token_signature = self ._build_auth_token_data (
125+ auth_token_ticket ,
126+ authenticator ,
127+ private_key ,
128+ username = username ,
129+ )
105130 logging .debug ('[CAS] AuthToken: {}' .format (auth_token ))
106-
107- digest = SHA256 .new ()
108- digest .update (auth_token )
109- auth_token = base64 .b64encode (auth_token )
110-
111- auth_token_signature = signer .sign (digest )
112- auth_token_signature = base64 .b64encode (auth_token_signature )
113-
114131 url = self ._get_auth_token_login_url (
115132 auth_token = auth_token ,
116133 auth_token_signature = auth_token_signature ,
@@ -185,30 +202,6 @@ def get_logout_url(self, service_url=None):
185202 logging .debug ('[CAS] Logout URL: {}' .format (url ))
186203 return url
187204
188- def perform_proxy (self , proxy_ticket ):
189- '''
190- Fetch a response from the remote CAS `proxy` endpoint.
191- '''
192- url = self ._get_proxy_url (ticket = proxy_ticket )
193- logging .debug ('[CAS] Proxy URL: {}' .format (url ))
194- return self ._perform_cas_call (url , ticket = proxy_ticket )
195-
196- def perform_proxy_validate (self , proxied_service_ticket ):
197- '''
198- Fetch a response from the remote CAS `proxyValidate` endpoint.
199- '''
200- url = self ._get_proxy_validate_url (ticket = proxied_service_ticket )
201- logging .debug ('[CAS] ProxyValidate URL: {}' .format (url ))
202- return self ._perform_cas_call (url , ticket = proxied_service_ticket )
203-
204- def perform_service_validate (self , ticket = None , service_url = None ):
205- '''
206- Fetch a response from the remote CAS `serviceValidate` endpoint.
207- '''
208- url = self ._get_service_validate_url (ticket , service_url = service_url )
209- logging .debug ('[CAS] ServiceValidate URL: {}' .format (url ))
210- return self ._perform_cas_call (url , ticket = ticket )
211-
212205 def parse_logout_request (self , message_text ):
213206 '''
214207 Parse the contents of a CAS `LogoutRequest` XML message.
@@ -256,6 +249,58 @@ def parse_logout_request(self, message_text):
256249 ))
257250 return result
258251
252+ def perform_api_request (
253+ self ,
254+ api_resource ,
255+ auth_token_ticket ,
256+ authenticator ,
257+ private_key ,
258+ method = 'POST' ,
259+ service_url = None ,
260+ ** kwargs
261+ ):
262+ '''
263+ Perform an auth-token-protected request against a CAS API endpoint.
264+ '''
265+ assert method in ('GET' , 'POST' )
266+ url = self .get_api_url (
267+ api_resource ,
268+ auth_token_ticket ,
269+ authenticator ,
270+ private_key ,
271+ service_url = None ,
272+ ** kwargs
273+ )
274+ if method == 'GET' :
275+ response = self ._perform_get (url )
276+ elif method == 'POST' :
277+ response = self ._perform_post (url )
278+ return response
279+
280+ def perform_proxy (self , proxy_ticket ):
281+ '''
282+ Fetch a response from the remote CAS `proxy` endpoint.
283+ '''
284+ url = self ._get_proxy_url (ticket = proxy_ticket )
285+ logging .debug ('[CAS] Proxy URL: {}' .format (url ))
286+ return self ._perform_cas_call (url , ticket = proxy_ticket )
287+
288+ def perform_proxy_validate (self , proxied_service_ticket ):
289+ '''
290+ Fetch a response from the remote CAS `proxyValidate` endpoint.
291+ '''
292+ url = self ._get_proxy_validate_url (ticket = proxied_service_ticket )
293+ logging .debug ('[CAS] ProxyValidate URL: {}' .format (url ))
294+ return self ._perform_cas_call (url , ticket = proxied_service_ticket )
295+
296+ def perform_service_validate (self , ticket = None , service_url = None ):
297+ '''
298+ Fetch a response from the remote CAS `serviceValidate` endpoint.
299+ '''
300+ url = self ._get_service_validate_url (ticket , service_url = service_url )
301+ logging .debug ('[CAS] ServiceValidate URL: {}' .format (url ))
302+ return self ._perform_cas_call (url , ticket = ticket )
303+
259304 def session_exists (self , ticket ):
260305 '''
261306 Test if a session records exists for a service ticket.
@@ -267,6 +312,30 @@ def session_exists(self, ticket):
267312
268313 ### PRIVATE METHODS ###
269314
315+ def _build_auth_token_data (
316+ self ,
317+ auth_token_ticket ,
318+ authenticator ,
319+ private_key ,
320+ ** kwargs
321+ ):
322+ auth_token = dict (
323+ authenticator = authenticator ,
324+ ticket = auth_token_ticket ,
325+ ** kwargs
326+ )
327+ auth_token = json .dumps (auth_token , sort_keys = True )
328+ if six .PY3 :
329+ auth_token = auth_token .encode ('utf-8' )
330+ digest = SHA256 .new ()
331+ digest .update (auth_token )
332+ auth_token = base64 .b64encode (auth_token )
333+ rsa_key = RSA .importKey (private_key )
334+ signer = PKCS1_v1_5 .new (rsa_key )
335+ auth_token_signature = signer .sign (digest )
336+ auth_token_signature = base64 .b64encode (auth_token_signature )
337+ return auth_token , auth_token_signature
338+
270339 def _clean_up_response_text (self , response_text ):
271340 lines = []
272341 for line in response_text .splitlines ():
@@ -275,14 +344,18 @@ def _clean_up_response_text(self, response_text):
275344 lines .append (line )
276345 return '\n ' .join (lines )
277346
278- def _get_auth_token_tickets_url (self ):
279- template = '{server_url}{auth_prefix}/api/auth_token_tickets '
347+ def _get_api_url (self , api_resource ):
348+ template = '{server_url}{auth_prefix}/api/{api_resource} '
280349 url = template .format (
350+ api_resource = api_resource ,
281351 auth_prefix = self .auth_prefix ,
282352 server_url = self .server_url ,
283353 )
284354 return url
285355
356+ def _get_auth_token_tickets_url (self ):
357+ return self ._get_api_url ('auth_token_tickets' )
358+
286359 def _get_auth_token_login_url (self , auth_token , auth_token_signature , service_url ):
287360 template = '{server_url}{auth_prefix}/authTokenLogin?{query_string}'
288361 query_string = urlencode ({
0 commit comments