Compare commits
11 commits
master
...
cameron.wh
Author | SHA1 | Date | |
---|---|---|---|
|
1cf4731653 | ||
|
752beb1e12 | ||
|
9d7107af1c | ||
|
5bc72d6940 | ||
|
fcf2093004 | ||
|
afc2d6b846 | ||
|
3a0b40a592 | ||
|
820b4ae5de | ||
|
f134e61261 | ||
|
cc0bd5006f | ||
|
286a275538 |
8 changed files with 402 additions and 77 deletions
15
README.md
15
README.md
|
@ -97,13 +97,14 @@ the `username` will be removed from the session.
|
|||
|
||||
#### Optional Configs ####
|
||||
|
||||
|Key | Default |
|
||||
|-------------------------|----------------|
|
||||
|CAS_TOKEN_SESSION_KEY | _CAS_TOKEN |
|
||||
|CAS_USERNAME_SESSION_KEY | CAS_USERNAME |
|
||||
|CAS_LOGIN_ROUTE | '/cas' |
|
||||
|CAS_LOGOUT_ROUTE | '/cas/logout' |
|
||||
|CAS_VALIDATE_ROUTE | '/cas/validate'|
|
||||
|Key | Default |
|
||||
|---------------------------|----------------|
|
||||
|CAS_TOKEN_SESSION_KEY | _CAS_TOKEN |
|
||||
|CAS_USERNAME_SESSION_KEY | CAS_USERNAME |
|
||||
|CAS_ATTRIBUTES_SESSION_KEY | CAS_ATTRIBUTES |
|
||||
|CAS_ROUTE_PREFIX | 'cas' |
|
||||
|CAS_LOGOUT_RETURN_URL | None |
|
||||
|CAS_VALIDATOR | 'validate' |
|
||||
|
||||
## Example ##
|
||||
|
||||
|
|
|
@ -27,13 +27,14 @@ class CAS(object):
|
|||
|
||||
Optional Configs:
|
||||
|
||||
|Key | Default |
|
||||
|-------------------------|----------------|
|
||||
|CAS_TOKEN_SESSION_KEY | _CAS_TOKEN |
|
||||
|CAS_USERNAME_SESSION_KEY | CAS_USERNAME |
|
||||
|CAS_LOGIN_ROUTE | '/cas' |
|
||||
|CAS_LOGOUT_ROUTE | '/cas/logout' |
|
||||
|CAS_VALIDATE_ROUTE | '/cas/validate'|
|
||||
|Key | Default |
|
||||
|---------------------------|----------------|
|
||||
|CAS_TOKEN_SESSION_KEY | _CAS_TOKEN |
|
||||
|CAS_USERNAME_SESSION_KEY | CAS_USERNAME |
|
||||
|CAS_ATTRIBUTES_SESSION_KEY | CAS_ATTRIBUTES |
|
||||
|CAS_ROUTE_PREFIX | '/cas' |
|
||||
|CAS_LOGOUT_RETURN_URL | None |
|
||||
|CAS_VALIDATOR | 'validate' |
|
||||
"""
|
||||
|
||||
def __init__(self, app=None, url_prefix=None):
|
||||
|
@ -45,9 +46,10 @@ class CAS(object):
|
|||
# Configuration defaults
|
||||
app.config.setdefault('CAS_TOKEN_SESSION_KEY', '_CAS_TOKEN')
|
||||
app.config.setdefault('CAS_USERNAME_SESSION_KEY', 'CAS_USERNAME')
|
||||
app.config.setdefault('CAS_LOGIN_ROUTE', '/cas')
|
||||
app.config.setdefault('CAS_LOGOUT_ROUTE', '/cas/logout')
|
||||
app.config.setdefault('CAS_VALIDATE_ROUTE', '/cas/validate')
|
||||
app.config.setdefault('CAS_ATTRIBUTES_SESSION_KEY', 'CAS_ATTRIBUTES')
|
||||
app.config.setdefault('CAS_ROUTE_PREFIX', 'cas')
|
||||
app.config.setdefault('CAS_LOGOUT_RETURN_URL', None)
|
||||
app.config.setdefault('CAS_VALIDATOR', 'validate')
|
||||
# Register Blueprint
|
||||
app.register_blueprint(routing.blueprint, url_prefix=url_prefix)
|
||||
|
||||
|
@ -60,7 +62,7 @@ class CAS(object):
|
|||
|
||||
def teardown(self, exception):
|
||||
ctx = stack.top
|
||||
|
||||
|
||||
@property
|
||||
def app(self):
|
||||
return self._app or current_app
|
||||
|
@ -70,8 +72,12 @@ class CAS(object):
|
|||
return flask.session.get(
|
||||
self.app.config['CAS_USERNAME_SESSION_KEY'], None)
|
||||
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
return flask.session.get(
|
||||
self.app.config['CAS_TOKEN_SESSION_KEY'], None)
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
return flask.session.get(
|
||||
self.app.config['CAS_ATTRIBUTES_SESSION_KEY'], {})
|
||||
|
|
|
@ -4,6 +4,8 @@ flask_cas.cas_urls
|
|||
Functions for creating urls to access CAS.
|
||||
"""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
try:
|
||||
from urllib import quote
|
||||
from urllib import urlencode
|
||||
|
@ -13,7 +15,6 @@ except ImportError:
|
|||
from urllib.parse import urljoin
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
def create_url(base, path=None, *query):
|
||||
""" Create a url.
|
||||
|
||||
|
@ -23,7 +24,7 @@ def create_url(base, path=None, *query):
|
|||
|
||||
Keyword arguments:
|
||||
base -- The left most part of the url (ex. http://localhost:5000).
|
||||
path -- The path after the base (ex. /foo/bar).
|
||||
path -- The path after the base (ex. /foo/bar or ['foo', 'bar']).
|
||||
query -- A list of key value pairs (ex. [('key', 'value')]).
|
||||
|
||||
Example usage:
|
||||
|
@ -35,10 +36,25 @@ def create_url(base, path=None, *query):
|
|||
... ('url', 'http://example.com'),
|
||||
... )
|
||||
'http://localhost:5000/foo/bar?key1=value&url=http%3A%2F%2Fexample.com'
|
||||
>>> create_url('http://localhost:5000/', ['/foo/', '/bar/'])
|
||||
'http://localhost:5000/foo/bar/'
|
||||
>>> create_url('http://localhost:5000', ['foo', 'bar'])
|
||||
'http://localhost:5000/foo/bar'
|
||||
>>> create_url('http://localhost:5000', ['/foo/', '/bar/'])
|
||||
'http://localhost:5000/foo/bar/'
|
||||
>>> create_url('http://localhost:5000/', ['foo/', '/bar/'])
|
||||
'http://localhost:5000/foo/bar/'
|
||||
>>> create_url('http://localhost:5000/', ['foo/', None, '/bar/'])
|
||||
'http://localhost:5000/foo/bar/'
|
||||
"""
|
||||
url = base
|
||||
# Add the path to the url if it's not None.
|
||||
if path is not None:
|
||||
# If path is a list remove all None values and reduce to a '/'
|
||||
# seperated string.
|
||||
if isinstance(path, list):
|
||||
path = filter(lambda x: bool(x), path)
|
||||
path = reduce(lambda l, r: '{}/{}'.format(l.rstrip('/'), r.lstrip('/')), path)
|
||||
# Add the path to the url if there is something to add.
|
||||
if path:
|
||||
url = urljoin(url, quote(path))
|
||||
# Remove key/value pairs with None values.
|
||||
query = filter(lambda pair: pair[1] is not None, query)
|
||||
|
@ -47,12 +63,13 @@ def create_url(base, path=None, *query):
|
|||
return url
|
||||
|
||||
|
||||
def create_cas_login_url(cas_url, cas_route, service, renew=None, gateway=None):
|
||||
def create_cas_login_url(cas_url, cas_route_prefix, service, renew=None,
|
||||
gateway=None, method=None):
|
||||
""" Create a CAS login URL .
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route -- The route where the CAS lives on server (ex. /cas)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
service -- (ex. http://localhost:5000/login)
|
||||
renew -- "true" or "false"
|
||||
gateway -- "true" or "false"
|
||||
|
@ -60,50 +77,51 @@ def create_cas_login_url(cas_url, cas_route, service, renew=None, gateway=None):
|
|||
Example usage:
|
||||
>>> create_cas_login_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... '/cas',
|
||||
... 'cas',
|
||||
... 'http://localhost:5000',
|
||||
... )
|
||||
'http://sso.pdx.edu/cas?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
'http://sso.pdx.edu/cas/login?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
cas_route,
|
||||
[cas_route_prefix, 'login'],
|
||||
('service', service),
|
||||
('renew', renew),
|
||||
('gateway', gateway),
|
||||
('method', method),
|
||||
)
|
||||
|
||||
|
||||
def create_cas_logout_url(cas_url, cas_route, url=None):
|
||||
def create_cas_logout_url(cas_url, cas_route_prefix, service=None):
|
||||
""" Create a CAS logout URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route -- The route where the CAS lives on server (ex. /cas/logout)
|
||||
url -- (ex. http://localhost:5000/login)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
service -- (ex. http://localhost:5000/login)
|
||||
|
||||
Example usage:
|
||||
>>> create_cas_logout_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... '/cas/logout',
|
||||
... 'cas',
|
||||
... 'http://localhost:5000',
|
||||
... )
|
||||
'http://sso.pdx.edu/cas/logout?url=http%3A%2F%2Flocalhost%3A5000'
|
||||
'http://sso.pdx.edu/cas/logout?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
cas_route,
|
||||
('url', url),
|
||||
[cas_route_prefix, 'logout'],
|
||||
('service', service),
|
||||
)
|
||||
|
||||
|
||||
def create_cas_validate_url(cas_url, cas_route, service, ticket,
|
||||
def create_cas_validate_url(cas_url, cas_route_prefix, service, ticket,
|
||||
renew=None):
|
||||
""" Create a CAS validate URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route -- The route where the CAS lives on server (ex. /cas/validate)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
service -- (ex. http://localhost:5000/login)
|
||||
ticket -- (ex. 'ST-58274-x839euFek492ou832Eena7ee-cas')
|
||||
renew -- "true" or "false"
|
||||
|
@ -111,7 +129,7 @@ def create_cas_validate_url(cas_url, cas_route, service, ticket,
|
|||
Example usage:
|
||||
>>> create_cas_validate_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... '/cas/validate',
|
||||
... 'cas',
|
||||
... 'http://localhost:5000/login',
|
||||
... 'ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
... )
|
||||
|
@ -119,8 +137,119 @@ def create_cas_validate_url(cas_url, cas_route, service, ticket,
|
|||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
cas_route,
|
||||
[cas_route_prefix, 'validate'],
|
||||
('service', service),
|
||||
('ticket', ticket),
|
||||
('renew', renew),
|
||||
)
|
||||
|
||||
def create_cas_serviceValidate_url(cas_url, cas_route_prefix, service, ticket,
|
||||
pgtUrl=None, renew=None):
|
||||
|
||||
""" Create a CAS serviceValidate URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
service -- (ex. http://localhost:5000/login)
|
||||
ticket -- (ex. 'ST-58274-x839euFek492ou832Eena7ee-cas')
|
||||
pgtUrl -- The url of the proxy callback
|
||||
renew -- "true" or "false"
|
||||
|
||||
Example usage:
|
||||
>>> create_cas_serviceValidate_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... 'cas',
|
||||
... 'http://localhost:5000/login',
|
||||
... 'ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
... )
|
||||
'http://sso.pdx.edu/cas/serviceValidate?service=http%3A%2F%2Flocalhost%3A5000%2Flogin&ticket=ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
[cas_route_prefix, 'serviceValidate'],
|
||||
('service', service),
|
||||
('ticket', ticket),
|
||||
('pgtUrl', pgtUrl),
|
||||
('renew', renew),
|
||||
)
|
||||
|
||||
def create_cas_proxyValidate_url(cas_url, cas_route_prefix, service, ticket,
|
||||
pgtUrl=None, renew=None):
|
||||
|
||||
""" Create a CAS proxyValidate URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
service -- (ex. http://localhost:5000/login)
|
||||
ticket -- (ex. 'ST-58274-x839euFek492ou832Eena7ee-cas')
|
||||
pgtUrl -- The url of the proxy callback
|
||||
renew -- "true" or "false"
|
||||
|
||||
Example usage:
|
||||
>>> create_cas_proxyValidate_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... 'cas',
|
||||
... 'http://localhost:5000/login',
|
||||
... 'ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
... )
|
||||
'http://sso.pdx.edu/cas/proxyValidate?service=http%3A%2F%2Flocalhost%3A5000%2Flogin&ticket=ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
[cas_route_prefix, 'proxyValidate'],
|
||||
('service', service),
|
||||
('ticket', ticket),
|
||||
('pgtUrl', pgtUrl),
|
||||
('renew', renew),
|
||||
)
|
||||
|
||||
def create_cas_proxy_url(cas_url, cas_route_prefix, pgt, targetService):
|
||||
|
||||
""" Create a CAS proxy URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
pgt -- The proxy-granting ticket
|
||||
targetService -- The service identifier of the back-end service
|
||||
|
||||
Example usage:
|
||||
>>> create_cas_proxy_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... 'cas',
|
||||
... 'PGT-490649-W81Y9Sa2vTM7hda7xNTkezTbVge4CUsybAr',
|
||||
... 'http://www.service.com',
|
||||
... )
|
||||
'http://sso.pdx.edu/cas/proxy?pgt=PGT-490649-W81Y9Sa2vTM7hda7xNTkezTbVge4CUsybAr&targetService=http%3A%2F%2Fwww.service.com'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
[cas_route_prefix, 'proxy'],
|
||||
('pgt', pgt),
|
||||
('targetService', targetService),
|
||||
)
|
||||
|
||||
def create_cas_samIValidate_url(cas_url, cas_route_prefix, target):
|
||||
|
||||
""" Create a CAS samIValidate URL.
|
||||
|
||||
Keyword arguments:
|
||||
cas_url -- The url to the CAS (ex. http://sso.pdx.edu)
|
||||
cas_route_prefix -- The prefix of the CAS endpoint (ex. /cas/)
|
||||
target -- The url of the back-end service
|
||||
|
||||
Example usage:
|
||||
>>> create_cas_samIValidate_url(
|
||||
... 'http://sso.pdx.edu',
|
||||
... 'cas',
|
||||
... 'http://www.target.com',
|
||||
... )
|
||||
'http://sso.pdx.edu/cas/samIValidate?target=http%3A%2F%2Fwww.target.com'
|
||||
"""
|
||||
return create_url(
|
||||
cas_url,
|
||||
[cas_route_prefix, 'samIValidate'],
|
||||
('target', target),
|
||||
)
|
||||
|
|
|
@ -3,7 +3,9 @@ from flask import current_app
|
|||
from .cas_urls import create_cas_login_url
|
||||
from .cas_urls import create_cas_logout_url
|
||||
from .cas_urls import create_cas_validate_url
|
||||
from .cas_urls import create_cas_serviceValidate_url
|
||||
|
||||
from xml.etree import ElementTree
|
||||
|
||||
try:
|
||||
from urllib import urlopen
|
||||
|
@ -31,7 +33,7 @@ def login():
|
|||
|
||||
redirect_url = create_cas_login_url(
|
||||
current_app.config['CAS_SERVER'],
|
||||
current_app.config['CAS_LOGIN_ROUTE'],
|
||||
current_app.config['CAS_ROUTE_PREFIX'],
|
||||
flask.url_for('.login', _external=True))
|
||||
|
||||
if 'ticket' in flask.request.args:
|
||||
|
@ -63,13 +65,17 @@ def logout():
|
|||
|
||||
redirect_url = create_cas_logout_url(
|
||||
current_app.config['CAS_SERVER'],
|
||||
current_app.config['CAS_LOGOUT_ROUTE'])
|
||||
current_app.config['CAS_ROUTE_PREFIX'],
|
||||
current_app.config['CAS_LOGOUT_RETURN_URL'],
|
||||
)
|
||||
|
||||
current_app.logger.debug('Redirecting to: {}'.format(redirect_url))
|
||||
return flask.redirect(redirect_url)
|
||||
|
||||
#current_app.logger.debug("Making GET request to {}".format(
|
||||
# cas_validate_url))
|
||||
|
||||
def validate(ticket):
|
||||
def validator(ticket):
|
||||
"""
|
||||
Will attempt to validate the ticket. If validation fails, then False
|
||||
is returned. If validation is successful, then True is returned
|
||||
|
@ -77,31 +83,93 @@ def validate(ticket):
|
|||
key `CAS_USERNAME_SESSION_KEY`.
|
||||
"""
|
||||
|
||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||
validators = {
|
||||
'validate': validate,
|
||||
'serviceValidate': serviceValidate,
|
||||
}
|
||||
|
||||
current_app.logger.debug("validating token {}".format(ticket))
|
||||
|
||||
try:
|
||||
is_valid = validators[current_app.config['CAS_VALIDATOR']](ticket)
|
||||
|
||||
if is_valid:
|
||||
current_app.logger.debug("valid")
|
||||
else:
|
||||
current_app.logger.debug("invalid")
|
||||
|
||||
return is_valid
|
||||
|
||||
except KeyError:
|
||||
raise ValueError('Unsupported CAS_VALIDATOR %r' % current_app.config['CAS_VALIDATOR'])
|
||||
|
||||
def validate(ticket):
|
||||
|
||||
cas_validate_url = create_cas_validate_url(
|
||||
current_app.config['CAS_SERVER'],
|
||||
current_app.config['CAS_VALIDATE_ROUTE'],
|
||||
current_app.config['CAS_ROUTE_PREFIX'],
|
||||
flask.url_for('.login', _external=True),
|
||||
ticket)
|
||||
|
||||
current_app.logger.debug("Making GET request to {}".format(
|
||||
cas_validate_url))
|
||||
response = urlopen(cas_validate_url)
|
||||
|
||||
try:
|
||||
(isValid, username) = urlopen(cas_validate_url).readlines()
|
||||
isValid = True if isValid.strip() == b'yes' else False
|
||||
username = username.strip().decode('utf8', 'ignore')
|
||||
(is_valid, username) = response.readlines()
|
||||
is_valid = True if is_valid.strip() == b'yes' else False
|
||||
if is_valid:
|
||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||
username = username.strip().decode('utf8', 'ignore')
|
||||
flask.session[cas_username_session_key] = username
|
||||
except ValueError:
|
||||
current_app.logger.error("CAS returned unexpected result")
|
||||
isValid = False
|
||||
is_valid = False
|
||||
|
||||
if isValid:
|
||||
current_app.logger.debug("valid")
|
||||
flask.session[cas_username_session_key] = username
|
||||
else:
|
||||
current_app.logger.debug("invalid")
|
||||
response.close()
|
||||
|
||||
return isValid
|
||||
return is_valid
|
||||
|
||||
|
||||
def serviceValidate(ticket):
|
||||
|
||||
cas_serviceValidate_url = create_cas_serviceValidate_url(
|
||||
current_app.config['CAS_SERVER'],
|
||||
current_app.config['CAS_ROUTE_PREFIX'],
|
||||
flask.url_for('.login', _external=True),
|
||||
ticket)
|
||||
|
||||
response = urlopen(cas_serviceValidate_url)
|
||||
|
||||
try:
|
||||
data = response.read()
|
||||
tree = ElementTree.fromstring(data)
|
||||
user = tree.find('*/cas:user', namespaces=dict(cas='http://www.yale.edu/tp/cas'))
|
||||
is_valid = user != None
|
||||
if is_valid:
|
||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||
cas_attributes_session_key = current_app.config['CAS_ATTRIBUTES_SESSION_KEY']
|
||||
attributes = {}
|
||||
username = user.text
|
||||
attrs = tree.find('*/cas:attributes', namespaces=dict(cas='http://www.yale.edu/tp/cas'))
|
||||
if attrs:
|
||||
for attr in attrs:
|
||||
tag = attr.tag.split("}").pop()
|
||||
if tag in attributes:
|
||||
# found multiple value attribute
|
||||
if isinstance(attributes[tag], list):
|
||||
attributes[tag].append(attr.text)
|
||||
else:
|
||||
attributes[tag] = [attributes[tag], attr.text]
|
||||
else:
|
||||
attributes[tag] = attr.text
|
||||
flask.session[cas_username_session_key] = username
|
||||
flask.session[cas_attributes_session_key] = attributes
|
||||
return True
|
||||
else:
|
||||
error = tree.find('cas:authenticationFailure', namespaces=dict(cas='http://www.yale.edu/tp/cas'))
|
||||
if error is None:
|
||||
current_app.logger.error('Error: Unknown response, ' + data)
|
||||
else:
|
||||
current_app.logger.error('Error: ' + error.get('code') + ', ' + error.text)
|
||||
return False
|
||||
finally:
|
||||
response.close()
|
||||
|
|
2
setup.py
2
setup.py
|
@ -8,7 +8,7 @@ import textwrap
|
|||
if __name__ == "__main__":
|
||||
setuptools.setup(
|
||||
name="Flask-CAS",
|
||||
version="0.4.3",
|
||||
version="1.0.0",
|
||||
description="Flask extension for CAS",
|
||||
author="Cameron Brandon White",
|
||||
author_email="cameronbwhite90@gmail.com",
|
||||
|
|
|
@ -118,7 +118,7 @@ class test_create_cas_login_url(unittest.TestCase):
|
|||
'/cas',
|
||||
'http://localhost:5000',
|
||||
),
|
||||
'http://sso.pdx.edu/cas?service=http%3A%2F%2Flocalhost%3A5000',
|
||||
'http://sso.pdx.edu/cas/login?service=http%3A%2F%2Flocalhost%3A5000',
|
||||
)
|
||||
|
||||
def test_with_renew(self):
|
||||
|
@ -129,7 +129,7 @@ class test_create_cas_login_url(unittest.TestCase):
|
|||
'http://localhost:5000',
|
||||
renew="true",
|
||||
),
|
||||
'http://sso.pdx.edu/cas?service=http%3A%2F%2Flocalhost%3A5000&renew=true',
|
||||
'http://sso.pdx.edu/cas/login?service=http%3A%2F%2Flocalhost%3A5000&renew=true',
|
||||
)
|
||||
|
||||
def test_with_gateway(self):
|
||||
|
@ -140,7 +140,7 @@ class test_create_cas_login_url(unittest.TestCase):
|
|||
'http://localhost:5000',
|
||||
gateway="true",
|
||||
),
|
||||
'http://sso.pdx.edu/cas?service=http%3A%2F%2Flocalhost%3A5000&gateway=true',
|
||||
'http://sso.pdx.edu/cas/login?service=http%3A%2F%2Flocalhost%3A5000&gateway=true',
|
||||
)
|
||||
|
||||
def test_with_renew_and_gateway(self):
|
||||
|
@ -152,7 +152,7 @@ class test_create_cas_login_url(unittest.TestCase):
|
|||
renew="true",
|
||||
gateway="true",
|
||||
),
|
||||
'http://sso.pdx.edu/cas?service=http%3A%2F%2Flocalhost%3A5000&renew=true&gateway=true',
|
||||
'http://sso.pdx.edu/cas/login?service=http%3A%2F%2Flocalhost%3A5000&renew=true&gateway=true',
|
||||
)
|
||||
|
||||
|
||||
|
@ -162,7 +162,7 @@ class test_create_cas_logout_url(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
create_cas_logout_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/logout'
|
||||
'/cas/'
|
||||
),
|
||||
'http://sso.pdx.edu/cas/logout',
|
||||
)
|
||||
|
@ -171,12 +171,31 @@ class test_create_cas_logout_url(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
create_cas_logout_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/logout',
|
||||
'/cas',
|
||||
'http://localhost:5000',
|
||||
),
|
||||
'http://sso.pdx.edu/cas/logout?url=http%3A%2F%2Flocalhost%3A5000'
|
||||
'http://sso.pdx.edu/cas/logout?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
)
|
||||
|
||||
def test_with_url_for_cas2(self):
|
||||
self.assertEqual(
|
||||
create_cas_logout_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/',
|
||||
'http://localhost:5000',
|
||||
),
|
||||
'http://sso.pdx.edu/cas/logout?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
)
|
||||
|
||||
def test_with_url_cas3(self):
|
||||
self.assertEqual(
|
||||
create_cas_logout_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/',
|
||||
'http://localhost:5000',
|
||||
),
|
||||
'http://sso.pdx.edu/cas/logout?service=http%3A%2F%2Flocalhost%3A5000'
|
||||
)
|
||||
|
||||
class test_create_cas_validate_url(unittest.TestCase):
|
||||
|
||||
|
@ -184,7 +203,7 @@ class test_create_cas_validate_url(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
create_cas_validate_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/validate',
|
||||
'/cas/',
|
||||
'http://localhost:5000/login',
|
||||
'ST-58274-x839euFek492ou832Eena7ee-cas'
|
||||
),
|
||||
|
@ -195,7 +214,7 @@ class test_create_cas_validate_url(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
create_cas_validate_url(
|
||||
'http://sso.pdx.edu',
|
||||
'/cas/validate',
|
||||
'/cas/',
|
||||
'http://localhost:5000/login',
|
||||
'ST-58274-x839euFek492ou832Eena7ee-cas',
|
||||
renew='true',
|
||||
|
|
|
@ -20,7 +20,7 @@ class test_flask_cas(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
|
||||
def test_cas_constructor_with_url_prefix(self):
|
||||
self.app = flask.Flask(__name__)
|
||||
|
@ -37,7 +37,7 @@ class test_flask_cas(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Fcas%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Fcas%2Flogin%2F')
|
||||
|
||||
def test_cas_constructor_properties(self):
|
||||
|
||||
|
@ -69,7 +69,7 @@ class test_flask_cas(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
|
||||
def test_cas_init_app_with_prefix_url(self):
|
||||
self.app = flask.Flask(__name__)
|
||||
|
@ -87,7 +87,7 @@ class test_flask_cas(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Fcas%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Fcas%2Flogin%2F')
|
||||
|
||||
def test_cas_init_app_properties(self):
|
||||
|
||||
|
|
|
@ -28,13 +28,10 @@ class test_routing(unittest.TestCase):
|
|||
self.app.config['CAS_SERVER'] = 'http://cas.server.com'
|
||||
self.app.config['CAS_TOKEN_SESSION_KEY'] = '_CAS_TOKEN'
|
||||
self.app.config['CAS_USERNAME_SESSION_KEY'] = 'CAS_USERNAME'
|
||||
self.app.config['CAS_ATTRIBUTES_SESSION_KEY'] = 'CAS_ATTRIBUTES'
|
||||
self.app.config['CAS_AFTER_LOGIN'] = 'root'
|
||||
self.app.config['CAS_LOGIN_ROUTE'] = '/cas'
|
||||
self.app.config['CAS_LOGOUT_ROUTE'] = '/cas/logout'
|
||||
self.app.config['CAS_VALIDATE_ROUTE'] = '/cas/validate'
|
||||
self.app.config['CAS_ROUTE_PREFIX'] = 'cas'
|
||||
|
||||
def test_setUp(self):
|
||||
pass
|
||||
|
||||
def test_login_by_logged_out_user(self):
|
||||
with self.app.test_client() as client:
|
||||
|
@ -42,7 +39,7 @@ class test_routing(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
|
||||
@mock.patch.object(routing, 'urlopen',
|
||||
return_value=io.BytesIO(b'yes\nbob\n'))
|
||||
|
@ -95,7 +92,7 @@ class test_routing(unittest.TestCase):
|
|||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
'http://cas.server.com/cas/login?service=http%3A%2F%2Flocalhost%2Flogin%2F')
|
||||
|
||||
def test_logout(self):
|
||||
with self.app.test_client() as client:
|
||||
|
@ -105,6 +102,16 @@ class test_routing(unittest.TestCase):
|
|||
response.headers['Location'],
|
||||
'http://cas.server.com/cas/logout')
|
||||
|
||||
def test_logout_with_return_url(self):
|
||||
with self.app.test_client() as client:
|
||||
self.app.config['CAS_VERSION'] = '2'
|
||||
self.app.config['CAS_LOGOUT_RETURN_URL'] = 'http://example.com'
|
||||
response = client.get('/logout/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.headers['Location'],
|
||||
'http://cas.server.com/cas/logout?service=http%3A%2F%2Fexample.com')
|
||||
|
||||
@mock.patch.object(routing, 'urlopen',
|
||||
return_value=io.BytesIO(b'yes\nbob\n'))
|
||||
def test_validate_valid(self, m):
|
||||
|
@ -127,3 +134,98 @@ class test_routing(unittest.TestCase):
|
|||
self.assertNotIn(
|
||||
self.app.config['CAS_TOKEN_SESSION_KEY'],
|
||||
flask.session)
|
||||
|
||||
@mock.patch.object(
|
||||
routing,
|
||||
'urlopen',
|
||||
return_value=io.BytesIO(b"""
|
||||
<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
|
||||
<cas:authenticationSuccess>
|
||||
<cas:user>bob</cas:user>
|
||||
<cas:proxyGrantingTicket>PGTIOU-84678-8a9d...</cas:proxyGrantingTicket>
|
||||
</cas:authenticationSuccess>
|
||||
</cas:serviceResponse>"""))
|
||||
def test_serviceValidate_valid(self, m):
|
||||
with self.app.test_request_context('/login/'):
|
||||
ticket = '12345-abcdefg-cas'
|
||||
self.assertEqual(routing.serviceValidate(ticket), True)
|
||||
self.assertEqual(
|
||||
self.cas.username,
|
||||
'bob')
|
||||
|
||||
@mock.patch.object(
|
||||
routing,
|
||||
'urlopen',
|
||||
return_value=io.BytesIO(b"""
|
||||
<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
|
||||
<cas:authenticationFailure code="INVALID_TICKET">
|
||||
Ticket ST-1856339-aA5Yuvrxzpv8Tau1cYQ7 not recognized
|
||||
</cas:authenticationFailure>
|
||||
</cas:serviceResponse>"""))
|
||||
def test_serviceValidate_invalid(self, m):
|
||||
self.app.config['CAS_VALIDATOR'] = 'serviceValidate'
|
||||
with self.app.test_request_context('/login/'):
|
||||
ticket = '12345-abcdefg-cas'
|
||||
self.assertEqual(routing.serviceValidate(ticket), False)
|
||||
self.assertNotIn(
|
||||
self.app.config['CAS_USERNAME_SESSION_KEY'],
|
||||
flask.session)
|
||||
self.assertNotIn(
|
||||
self.app.config['CAS_TOKEN_SESSION_KEY'],
|
||||
flask.session)
|
||||
|
||||
|
||||
@mock.patch.object(
|
||||
routing,
|
||||
'urlopen',
|
||||
return_value=io.BytesIO(b"""
|
||||
<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
|
||||
<cas:authenticationSuccess>
|
||||
<cas:user>bob</cas:user>
|
||||
<cas:attributes>
|
||||
<cas:firstname>John</cas:firstname>
|
||||
<cas:lastname>Doe</cas:lastname>
|
||||
<cas:title>Mr.</cas:title>
|
||||
<cas:email>jdoe@example.org</cas:email>
|
||||
<cas:affiliation>staff</cas:affiliation>
|
||||
<cas:affiliation>faculty</cas:affiliation>
|
||||
</cas:attributes>
|
||||
<cas:proxyGrantingTicket>PGTIOU-84678-8a9d...</cas:proxyGrantingTicket>
|
||||
</cas:authenticationSuccess>
|
||||
</cas:serviceResponse>"""))
|
||||
def test_serviceValidate_with_attributes_valid(self, m):
|
||||
self.app.config['CAS_VALIDATOR'] = 'serviceValidate'
|
||||
with self.app.test_request_context('/login/'):
|
||||
ticket = '12345-abcdefg-cas'
|
||||
self.assertEqual(routing.serviceValidate(ticket), True)
|
||||
self.assertEqual(
|
||||
self.cas.username,
|
||||
'bob')
|
||||
self.assertEqual(
|
||||
self.cas.attributes,
|
||||
{'firstname': 'John',
|
||||
'lastname': 'Doe',
|
||||
'title': 'Mr.',
|
||||
'email': 'jdoe@example.org',
|
||||
'affiliation': ['staff', 'faculty']})
|
||||
|
||||
@mock.patch.object(
|
||||
routing,
|
||||
'urlopen',
|
||||
return_value=io.BytesIO(b"""
|
||||
<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
|
||||
<cas:authenticationFailure code="INVALID_TICKET">
|
||||
Ticket ST-1856339-aA5Yuvrxzpv8Tau1cYQ7 not recognized
|
||||
</cas:authenticationFailure>
|
||||
</cas:serviceResponse>"""))
|
||||
def test_serviceValidate_with_attributes_invalid(self, m):
|
||||
self.app.config['CAS_VALIDATOR'] = 'serviceValidate'
|
||||
with self.app.test_request_context('/login/'):
|
||||
ticket = '12345-abcdefg-cas'
|
||||
self.assertEqual(routing.serviceValidate(ticket), False)
|
||||
self.assertNotIn(
|
||||
self.app.config['CAS_USERNAME_SESSION_KEY'],
|
||||
flask.session)
|
||||
self.assertNotIn(
|
||||
self.app.config['CAS_TOKEN_SESSION_KEY'],
|
||||
flask.session)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue