cas-ng a small enhancement to cas 3 to provide new services
TRANSCRIPT
CAS-NG
A small enhancement to CAS 3to provide new services
Objectives [of this talk]
• TrustedOtherCas – single sign-on to more than one instance of the CAS codebase
• ScriptedValidate – Extend CAS ServiceValidate with Access Control rules written in simple scripting languages
• CAS Extensibility – How and where these extensions fit into the architecture
Enable Additional CAS Function Now
Existing Central
CAS 3 (or 2)
EnhancedCAS 3
Trust
ExistingSatisfiedServices
New Servicesrequiring
new function
ValidateValidate
Departmental Local Function
Existing Central
CAS 3 (or 2)
DepartmentalCAS 3
Trust
DepartmentService
Validate
DepartmentalCAS 3
Trust DepartmentService
Validate
CentralService
Simple Peer Federation
CAS
Service
Service
Campus
CAS
client
trust
Other Institution
cas-server-support-trustedexisting CAS 3 optional subproject
J2EE Container
WebListener
ContainerBased
Authentication
SERVLET
api
CAS
request.getRemoteUser()
trustedOtherCas – a WebFlow beanthat generates “trusted” credentials
CAS
login WebFlow/cas/logincookie
x.509 cert
other stuff
Form orOtherCAS
CAS Filter logic turned intoa Web Flow Bean
TrustedOtherCas Step by StepGet it in the WAR
• Get “cas-server-support-trusted-otherCAS” project, put it in CAS 3 source directory
• Add name to top level POM module list [so it gets compiled by Maven into a JAR]
• Add the “cas-server-support-trusted” and “trusted-otherCAS” artifact JARs to the webapp project POM dependency list [so JAR gets added to the WAR WEB-INF/lib]
TrustedOtherCas Step by StepSpring Configuration
• Add “trusted” project beans to the Handler and Resolver bean list [so credentials can be processed] in deployerConfigContext.xml
• In cas-servlet.xml, configure an instance of the trustedOtherCas bean with the login and validation URL of the other CAS
• Add OtherCas bean to login-webflow.xml and change flow logic to go to it
There can be more than one
• If you have more than one trustedOtherCas, each can have its own configured bean, but
• Each needs its own /loginXXX URL and its own WebFlow because the ticket= doesn’t tell you which CAS it came from, so you have to know this based on the URL that CAS redirected back to
Current CAS doesn’t do Access Control
Service created by Humanities Professor
Bin Laden front
end
CAS
Q: Who is this guy? A: “Bin Laden”
Should I let Bin Laden in?
All the institutional data about people isover there somewhere, but ordinary usersdon’t have access to it.
Allow access to licensed MP3 files to
• Music department faculty• Music graduate students• Undergraduate Music majors• Students enrolled in “Music 202”
Beyond the programming skills of a Music prof
Access Control Problems
• Don’t want to give out access to HR, student systems, and other institutional data to everyone who has a Web application
• Access control is too complicated for non-programmers to get it right
• XACML is irrational• Institutional logic: Just what is a …
CASNG makes the decisions
Bin Laden
Dumb Service
CASClient
withscript
uri
CAS
/cas/scriptedValidate,ticket=…, service=…,acscript={uri}
script
data getter
data getter
data getter
data getter
HRstudentsystem
FBI
Alumni
CAS 3Web (MVC)
View (JSP or Redirect)
Controller
Validate
LoginWebFlow
Ticket CRUDBusiness Logic
handlerhandlerhandler
resolverresolverresolver
TicketCache optional
storeAuthMgr
TrustedOtherCas scripts
Background: Spring MVC
CASmod ofSpringServlet
HTTP
Web.xmlservlet URL mappings
Bean
Bean
Bean
MVCURLmappings
Bean
Bean
LoginWebflow/login
/validate
Spring SideServlet Side
WEB-INF/web.xml URL mapping
• <servlet><servlet-name>cas</servlet-name><servlet-class>org.jasig.cas.web.init.SafeDispatcherServlet
• <servlet-mapping><servlet-name>cas</servlet-name><url-pattern>/scriptedValidate</url-pattern>
</servlet-mapping>
cas-servlet.xml then maps eachURL to a Spring Bean
<bean id="handlerMappingC" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="mappings"><props> <prop key="/serviceValidate“>serviceValidateController</prop> <prop key="/scriptedValidate“>scriptedValidateController</prop> <prop key="/validate“>legacyValidateController</prop>…<bean id="serviceValidateController" class="org.jasig.cas.web.ServiceValidateController"p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxying…"p:centralAuthenticationService-ref="centralAuthenticationService"p:proxyHandler-ref="proxy20Handler"p:argumentExtractor-ref="casArgumentExtractor" />
Spring MVC request lifecycle
SpringServlet
URL to bean map
Action Bean
Request
ModelAndView
Propertiesfile
JSP
(or View Bean)
ViewName
varname valuevarname valuevarname value
ModelEL
Plug it In
SpringServlet
URL to bean map
ScriptedValidateController
/scriptedValidate
ModelAndView
Propertiesfile
JSP
(or View Bean)
ViewName
varname valuevarname valuevarname value
ModelEL
ServiceValidate
Controller
CAS Business Logic APITicket CRUDBusiness Logic
handlerhandlerhandler
resolverresolverresolver
TicketCache optional
storeAuthMgr
If this was a real J2EE application, this would be the EJB layer
String createTicketGrantingTicket(Cred)String grantServiceTicket(st, Service)String grantServiceTicket(st, Service, Cred)Assertion validateServiceTicket(st, Service)void destroyTicketGrantingTicket(st)String delegateTicketGrantingTicket(st, Cred)
Credentials
• Credentials is a marker interface (no methods) added to any class that may authenticate a user (X.509 Cert, Password, …)
• A Handler validates the credentials (“The Cert was issued by a trusted CA”)
• A Resolver maps the Credential to a netid (by extracting the Netid from, say, the first CN in the Cert DN)
CAS APITicket CRUDBusiness Logic
handlerhandler
handler“I do”
resolverresolver
resolver“I do”
Ticket CacheAuth Mgr
“who handles this type of Credential?”
WebFlowAction Bean
returnsCredential
Principal
Spring WebFlow
• URL mapped to WebFlow [new XML]• Set Initial state• ActionState runs a bean or makes a EL test.
Success/Failure chooses new state• ViewStates display a Form, input goes to an
Action Bean• An EndState releases Flow scoped objects• Use for a single page is unexpected
Login Webflow
/loginIs a cookie/TGT provided
YesIssue ST
No
Gateway request
YesRedirect back
No
X.509 cert provided
YesCreate TGT
No
Display the FormSubmit
Password Valid No
YesCreate TGT
Trusted Other Cas
/loginIs a cookie/TGT provided
YesIssue ST
No
Gateway request
YesRedirect back
No
X.509 cert provided
YesCreate TGT
No
ticket= present (and validates to other CAS)
YesCreate TGT
No
Redirect to Other CAS
Add to WEB-INF/cas-servlet.xml
<bean id="trustedOtherCas" class=“…trusted.web.flow.PrincipalFromOtherCasNonInteractiveCredentialsAction" p:centralAuthenticationService-ref="centralAuthenticationService" p:loginUrl = "https://secure.its.yale.edu/cas/login" p:validateUrl = https://secure.its.yale.edu/cas/proxyValidate/>
WEB-INF/login-webflow.xml<action-state id="startAuthenticate"> <action bean="x509Check" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="tryOtherCas" /></action-state><action-state id="tryOtherCas"> <action bean="trustedOtherCas" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="otherCasRedirect" /></action-state><view-state id="viewLoginForm" view="casLoginView">…</view-state> <end-state id="otherCasRedirect“ view="bean:trustedOtherCas" />
Note: now you never get here
CAS WebFlow Bean
public final class PrincipalFromOtherCasNonInteractiveCredentialsAction extends AbstractNonInteractiveCredentialsAction implements ViewSelector {… protected Credentials
constructCredentialsFromRequest(…return new PrincipalBearingCredentials( new SimplePrincipal(remoteUser));
CAS internal API mapped to WebFlow concepts
• Return null follows “failure” state change(View method redirects to other CAS)
• Return Credentials follows “success” state change (to Create TGT)
• deployerConfigContext.xml must have cas-server-support-trusted Handler and Resolver that process this type of Credentials
There can be more than one
• If you have more than one trustedOtherCas, each can have its own configured bean, but
• Each needs its own /loginXXX URL and its own WebFlow because the ticket= doesn’t tell you which CAS it came from, so you have to know this based on the URL that CAS redirected back to
WEB-INF/deployerConfigContext.xml
<bean id="authenticationManager"class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<property name="credentialsToPrincipalResolvers"><list><bean class=“…principal.UsernamePasswordCredentialsToPrincipalResolver" /><bean class=“…trusted...PrincipalBearingCredentialsToPrincipalResolver" /><bean class=“…x509...X509CertificateCredentialsToIdentifierPrincipalResolver" p:identifier="$CN" />
<property name="authenticationHandlers"><list> <bean class=“…trusted...PrincipalBearingCredentialsAuthenticationHandler" /> <bean class=“…x509…X509CredentialsAuthenticationHandler“<bean class=“…JaasAuthenticationHandler" />
cas-server-webapp/pom.xml
<dependency><groupId>org.jasig.cas</groupId><artifactId>cas-server-support-trusted</artifactId><version>${project.version}</version></dependency><dependency><groupId>org.jasig.cas</groupId><artifactId>cas-server-support-trusted-otherCAS</artifactId><version>${project.version}</version></dependency><dependency><groupId>org.jasig.cas</groupId><artifactId>cas-server-scripting</artifactId><version>${project.version}</version></dependency>
Browser Comes to CAS 3
Test Existing Cookie X509 Windows login Redirect to CAS 2
Redirect to Other CAS (CAS 2)display FormValidate passwordIssue TGT cookieIssue ST for CAS3
Redirect back to CAS 3 (as Other CAS Service)
Validate CAS2 STCAS2 Netid becomesCAS3 principalIssue CAS3 TGT cookieIssue CAS 3 ST
Validateticket= in
cookie and ticket=back
Validate ST (with scripting)
Scriptlibrary
RhinoJavaScript
Engine
TicketCache
ScriptedValidateController
ServiceValidateController
Is ST valid?
Is access permitted?
handleRequestInternal()
Several ways to intercept the call to a single method
• Subclassing [requires removing “final” from parent class]
• Delegation: Create a separate ServiceValidateController bean instance
• AOP: Intercept the handleRequestInternal call, add script “advice” to the return
Success and Failure ViewNameproperties
• Subclass: one object (inherit the field)• Delegate: ScriptedValidate is configured, then
it sets the property value in the captive ServiceValidate object
• AOP: ???
cas-servlet.xml
<prop key="/scriptedValidate“>scriptedValidateController</prop>
<bean id="scriptedValidateController" class="org.jasig.cas.web.ScriptedValidateController“ [properties inherited from superclass ServiceValidateController] >
<property name="builders"> … List of beans that add variables to the JS environment
<property name="scripts"> .. . list of inline scripts keyed by URI
<property name="scriptResources"> … resource url of script files (file:. classpath:, http:, …)
There can be more than one
• You can have multiple /scriptedValidate URLs, with more than one Bean, with more than one configuration
• One for scripts with public data• One for more carefully controlled scripts with
access to more sensitive data• …
/scriptedValidate,acscript=“…uri…”,…
• Service (through the configured Filter init-param) designates a script by URI
• Spring XML configuration passes a Map to the bean. The keys are URIs. The values are– The inline text of the script– A file (local path, in WAR, http: URL)– A directory (if the URI key ends in “:*”)
The Directory Rule
• The CAS XML Map associates “cas:stuff:*” with /usr/local/casscripts/
• The Service sets acscript=cas:stuff:foo.js• CAS runs /usr/local/casscripts/foo.js
Java 6 JS (Rhino)
• Bindings contains a Map<String,Object>• Key becomes a JS variable name• Java object becomes a JS object (script can
reference properties, call methods)• Rhino adds built in packages. and java.
variables so script can use native Java objects, but we want to prevent access to local files and stuff.
Built-In JS Variables
• “java” and “packages” override• “netid” is Principal ID• “request” provided limited access to some
HttpServletRequest info (parameters)• “log” is log4j as in log.info(msg)
ObjectBuilder
public interface ObjectBuilder {
public abstract Map<String, Object> buildObjects(String netid);
LDAP Object Builder
<!-- Apache LDAP Directory Server running on localhost --><bean id="localLdapDao" class="edu.yale.its.tp.cas.scripting.LdapDao" p:ldapUrl="ldap://localhost:10389/dc=example,dc=com"p:ldapPassword = "yalescout"p:ldapUserid = "uid=yalescout,ou=users,ou=system"p:testLookup = "ou=system"p:netidAttribute = "uid"/>
<bean id="localLdap" class="edu.yale.its.tp.cas.scripting.LdapUserObjectBuilder"p:dao-ref="localLdapDao" p:variableName="mydir"/>
JDBC Object Builder
<bean id="employeeTable" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" /> <property name="url" value="jdbc:derby://localhost:1527/myeclipse" /> <property name="username" value="app" /> <property name="password" value="dummy" /> </bean> <bean id="jdbcUserBuilder" class="edu.yale.its.tp.cas.scripting.JdbcUserObjectBuilder" p:datasource-ref="employeeTable" p:query="select * from app.employee where netid=?" p:variableName="dbuser" />
ScriptedValidateController property
<property name="builders"><list><ref bean="localLdap" /><ref bean="jdbcUserBuilder" /></list></property>
ScriptedValidateController[run the script]
Object info = null;try {
info = engine.eval(scriptReader, bindings);} catch (Exception e) {
log.error("Error in the access control script: " + e);failClient("ScriptError","The access control script ended in error.", result);return result;
}
if (info instanceof String) {
Script Result
• The result of the script is the value of the last expression as in “drop dead”; or 666;
• Access permitted by “” or 0• Access refused if non-empty string or non zero
number, but• Nothing from the script is sent back to the
service, just the OK or rejection.
Prereqs
• Modified version of CAS 3 client – Generates the acscript= parameter– processes “ScriptReject” validation failure
response and turns it into a 403 Forbidden status.
Client<filter> <filter-name>CASValidateFilter</filter-name> <filter-class>org.jasig.cas.client.validation. Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://cas.example.com:8443/cas</param-value> </init-param><init-param> <param-name>serverName</param-name> <param-value>http://cas.example.com:8080</param-value> </init-param><init-param> <param-name>scriptUri</param-name> <param-value>cas:some:name</param-value> </init-param><init-param> <param-name>scriptParams</param-name> <param-value>group list or something</param-value> </init-param></filter>
“Research”
• Add prepackaged script URIsacscript=cas:isInAdGroup, acparam=wizards
• Remote debug error messages for a script developer
• Mostly, how to turn this from a working testbed into a finished product.