Package: botCasClient
botCasSP - A CAS Client for Bottle Web AppsDescriptionbotCasSPis aCAS (Central Authentication Service)client module forBottle web frameworkapplications.  TheCasSPinstance allows bottle apps to be CAS Service Providers (SP's), authenticating users to a CAS server, and use any assertions the CAS server provides within the client session.CASis alegacy single sign-on (SSO) protocol, still in not uncommon use, especially in academia. CAS uses back channel verification and is simple to add to applications.botCasSP Getting startedfrombotCasSPimportCasSPfromBottleSessionimportBottleSessionfrombottleimportBottlefromconfigimportsess_config,cas_configapp=Bottle()ses=BottleSession(*sess_config)cas_config={'cas_server_base_url':'https://cas.example.com/cas/','cas_attr_list':['sn','givenName','uid','groups']}casc=CasSP(app=app,config=cas_config)@app.route('/login')@casc.require_logindefhello():returnf'Hello{request.session['username']}'@app.route('/bobAndAlice')@casc.require_user(['bob','alice'])defonly():return'Hello bob or alice'@app.route('/sysadmins')@casc.require_attr('groups':['sysadmin','netadmin'])defadmins():return"Hello admin"@app.route('/logoff')defbye():ifcasc.is_authenticated:casc.initiate_logoff(next=request.url)return'bye'if__name__=='__main__':app.run()botCasSPusesBottleSessionsfor session management. InstallBottleSessionsbeforeCasSP. (BottleSessions is based onPallets projectcachelib, providing numerous caching back-ends including filesystem, redis, and memcached back-ends.)At the minimum (cas v1) the CAS server will just authenticate the user. Most v1 and all v2/v3 servers provide the username. WithbotCasSPthis can be accessed asrequest.session.get('username')orcasc.my_username.Other Information provided with CAS v2/v3 server authentication is matched against thecas_attr_listin the configuration. Attributes with matching names are added to the users session. This is['sn', 'givenName', 'uid', 'groups']in the example. If the CAS servers provides these attributes, they are included in the users session and are accessible in adictasrequest.session['attributes']orcasc.my_attrs.Theusernameandattributesdata are available both to views and to any middleware installed in the request stack afterBottleSessions.  This data can be used by Bottle apps to pre-populate forms, used for identifcation to other systems, etc.botCasSP CasSP ClassbotCasSPis the module,CasSPis the class implementingCAS Service Providerfunction.CasSP Class Signature:frombotCasSPimportCasSPcasc=CasSP(app=app,config=cas_config,sess_username='username',sess_attr='attributes',logger=None,)CasSP Parameters and Configuration:app:Required:the Bottle app instancesess_username='username':Optional:Name of the session entry to contain username (default:usernameas insession['username']) It's unlikely this needs to be changed.sess_attr='attributes':Optional:Name of the session entry to contain assertion attributes (default:attributesas insession['attributes']['email'])  It's unlikely this needs to be changed.config={}:Required:A Pythondictof CAS configuration parameters. The most important is the base url and the attribute list:cas_server_base_url: API base URL of the CAS server(required)Typically this is in the form ofhttps://cas.example.org/cas/. CasSP builds out the remainder of the server API from the base url.cas_version: CAS protocol version:v1,v2, orv3(default:v2) There isn't much benefit to v3, and v1 generally only provides the username.cas_attr_list: Pythonlistof attributes. These are the assertions from a v2 or v3 server to be kept in a users session from a successful CAS validation response.e.g.['email', 'sn', 'givenname', 'groups']It's up to the CAS server to provide parameters.defaultis [ ]Example cas_configcas_config={"cas_server_base_url":"https://cas.example.com/cas","cas_attr_list":["sn","given","email","groups","dept"]}logger=None:Optional:Pythonloggerobject. CasSP defaults logging tostderrif no logger is provided.Properties:casc.is_authenticated:Trueif the current session is authenticated.casc.my_usernameReturns username orNoneif not authenticated.casc.my_attrsReturnsdictof retrieved attrs or {} if not authenticated.Methods:Login managementcasc.initiate_login(next,**kwargs) => redirectReturns SAML (302) redirect to the CAS server to authenticate via username/password or SSO.next=None- URL to redirect after login completed (optional)Use this in your login view, or decorate with@casc.require_login- it does the same thing.casc.initiate_logout(next) => redirect | strInitiate Logout from iDP by redirecting to the CAS servers logout.next=None- URL to redirect after logout completed (optional) Some CAS servers ignore this.casc._finish_login() => Response | strroute:/casc/finishStandard service ticket endpoint fromcasc.initiate_login()Validates the ticket and runs login hooksConstruct user session data from validation response.redirects user to route that triggered the login.Login Hooks Decorator@casc.add_login_hookorcasc.add_login_hook(f)Decorates a functionfthat runs after SAML authentication is completedeach login hook is run in order of additondata can be updated before being added to the session with login_hooks@casc.add_login_hookdefmy_login_hook(username,attributes):# massage or transform attributesusername=username.tolower()# standaradize namingattributes['surname']=attributes['sn']delattributes['sn']# supplement data from other sourcesattributes['graph']=get_graph_data_api(username)# raise exception to thwart loginifattributes['affiliation']!='employee':raiseException('Employees only')# return both username and attributes for the next hook to use.returnusername,attributesView Decorators@casc.login_requiredorcasc.login_required(f)route decoratorDecorates a view functionfto require unauthenticated users to login.After authentication the user is redirected to the view that initaited the login.Order after @app.route decorators:@app.route('/login')@casc.login_requireddefmyview():return'All logged in!'@casc.require_user(user_or_list)orcasc.require_user(f, user_or_list)Decorates a functionfto require session user to be listeda single user or a list of users can be providedReturns403 Unauthorizedif session username is not in the listOrder after @app.route decorators:@app.route('/onlybob')@casc.require_user('bob')defview():return'Hi bob'@app.route('/boboralice')@casc.require_user(['bob','alice'])defview2():return'Hi alice or bob'@casc.require_attr(attr, value)orcasc.require_user(f, attr, value)Decorates a functionfto require session to have attr with value listeda single attribute value or a list of values can be providedReturns403 Unauthorizedif session does not have the required attr/value.Order after @app.route decorators:@app.route('/dbstuff')@casc.require_attr('role','dba')defdba_view():...@app.route('/infrastructure')@casc.require_attr('groups',['sysadmin','netadmin','storageadmin','cloudadmin'])definfra_view():...Proxy Mode MethodsThese are helpers for assembling a CAS proxy service, allowing an SP to request resources from another CAS server on a users behalf.casc.get_proxy_session(service, resource_urn)Acquire and authorize a proxy ticket forresource_urnviaserviceReturns aPython RequestsSessionobject orNonefor application use.RequestsSessionobject will have any of the service cookies for theresource_urnresource_urnis optional (will use service)casc.acquire_proxy_ticket(service)Returns Proxy Ticket for aserviceproxy_apimode only.casc.service_proxy()route:/casc/proxyAvailable only inproxy_apimode.Returns redirect for service with attached proxy_ticketroute front end forcasc.acquire_proxy_ticket