@Override protected void internalInit(final WebContext context) { CommonHelper.assertNotBlank("callbackUrl", this.callbackUrl); if (CommonHelper.isBlank(this.casLoginUrl) && CommonHelper.isBlank(this.casPrefixUrl)) { throw new TechnicalException("casLoginUrl and casPrefixUrl cannot be both blank"); } initializeClientConfiguration(); initializeLogoutHandler(context); if (this.casProtocol == CasProtocol.CAS10) { initializeCas10Protocol(); } else if (this.casProtocol == CasProtocol.CAS20) { initializeCas20Protocol(context); } else if (this.casProtocol == CasProtocol.CAS20_PROXY) { initializeCas20ProxyProtocol(context); } else if (this.casProtocol == CasProtocol.CAS30) { initializeCas30Protocol(context); } else if (this.casProtocol == CasProtocol.CAS30_PROXY) { initializeCas30ProxyProtocol(context); } else if (this.casProtocol == CasProtocol.SAML) { initializeSAMLProtocol(); } addAuthorizationGenerator(new DefaultCasAuthorizationGenerator<CasProfile>()); }
/** * Extracts digest Authorization header components. As per RFC 2617 : username is the user's name * in the specified realm qop is quality of protection uri is the request uri response is the * client response nonce is a server-specified data string which should be uniquely generated each * time a 401 response is made cnonce is the client nonce nc is the nonce count If in the * Authorization header it is not specified a username and response, we throw CredentialsException * because the client uses an username and a password to authenticate. response is just a MD5 * encoded value based on user provided password and RFC 2617 digest authentication encoding rules * * @param context the current web context * @return the Digest credentials */ @Override public DigestCredentials extract(WebContext context) throws HttpAction { final TokenCredentials credentials = this.extractor.extract(context); if (credentials == null) { return null; } String token = credentials.getToken(); Map<String, String> valueMap = parseTokenValue(token); String username = valueMap.get("username"); String response = valueMap.get("response"); if (CommonHelper.isBlank(username) || CommonHelper.isBlank(response)) { throw new CredentialsException("Bad format of the digest auth header"); } String realm = valueMap.get("realm"); String nonce = valueMap.get("nonce"); String uri = valueMap.get("uri"); String cnonce = valueMap.get("cnonce"); String nc = valueMap.get("nc"); String qop = valueMap.get("qop"); String method = context.getRequestMethod(); return new DigestCredentials( response, method, clientName, username, realm, nonce, uri, cnonce, nc, qop); }
protected void initializeClientConfiguration() { if (this.casPrefixUrl != null && !this.casPrefixUrl.endsWith("/")) { this.casPrefixUrl += "/"; } if (CommonHelper.isBlank(this.casPrefixUrl)) { this.casPrefixUrl = this.casLoginUrl.replaceFirst("/login$", "/"); } else if (CommonHelper.isBlank(this.casLoginUrl)) { this.casLoginUrl = this.casPrefixUrl + "login"; } }
protected void initializeClientConfiguration(final WebContext context) { if (this.casPrefixUrl != null && !this.casPrefixUrl.endsWith("/")) { this.casPrefixUrl += "/"; } if (CommonHelper.isBlank(this.casPrefixUrl)) { this.casPrefixUrl = this.casLoginUrl.replaceFirst("/login$", "/"); } else if (CommonHelper.isBlank(this.casLoginUrl)) { this.casLoginUrl = this.casPrefixUrl + "login"; } this.casPrefixUrl = callbackUrlResolver.compute(this.casPrefixUrl, context); this.casLoginUrl = callbackUrlResolver.compute(this.casLoginUrl, context); }
@Override public String getName() { if (CommonHelper.isBlank(this.name)) { return this.getClass().getSimpleName(); } return this.name; }
@Override protected void internalInit() { CommonHelper.assertNotBlank("callbackUrl", this.callbackUrl); CommonHelper.assertNotNull("logoutHandler", this.logoutHandler); if (CommonHelper.isBlank(this.casLoginUrl) && CommonHelper.isBlank(this.casPrefixUrl)) { throw new TechnicalException("casLoginUrl and casPrefixUrl cannot be both blank"); } if (this.casPrefixUrl != null && !this.casPrefixUrl.endsWith("/")) { this.casPrefixUrl += "/"; } if (CommonHelper.isBlank(this.casPrefixUrl)) { this.casPrefixUrl = this.casLoginUrl.replaceFirst("/login", "/"); } else if (CommonHelper.isBlank(this.casLoginUrl)) { this.casLoginUrl = this.casPrefixUrl + "login"; } if (this.casProtocol == CasProtocol.CAS10) { this.ticketValidator = new Cas10TicketValidator(this.casPrefixUrl); } else if (this.casProtocol == CasProtocol.CAS20) { this.ticketValidator = new Cas20ServiceTicketValidator(this.casPrefixUrl); if (this.casProxyReceptor != null) { final Cas20ServiceTicketValidator cas20ServiceTicketValidator = (Cas20ServiceTicketValidator) this.ticketValidator; cas20ServiceTicketValidator.setProxyCallbackUrl(this.casProxyReceptor.getCallbackUrl()); cas20ServiceTicketValidator.setProxyGrantingTicketStorage( this.casProxyReceptor.getProxyGrantingTicketStorage()); } } else if (this.casProtocol == CasProtocol.CAS20_PROXY) { this.ticketValidator = new Cas20ProxyTicketValidator(this.casPrefixUrl); final Cas20ProxyTicketValidator cas20ProxyTicketValidator = (Cas20ProxyTicketValidator) this.ticketValidator; cas20ProxyTicketValidator.setAcceptAnyProxy(this.acceptAnyProxy); cas20ProxyTicketValidator.setAllowedProxyChains(this.allowedProxyChains); if (this.casProxyReceptor != null) { cas20ProxyTicketValidator.setProxyCallbackUrl(this.casProxyReceptor.getCallbackUrl()); cas20ProxyTicketValidator.setProxyGrantingTicketStorage( this.casProxyReceptor.getProxyGrantingTicketStorage()); } } else if (this.casProtocol == CasProtocol.SAML) { this.ticketValidator = new Saml11TicketValidator(this.casPrefixUrl); } }
@Override public void validate(final TokenCredentials credentials) throws HttpAction { if (credentials == null) { throw new CredentialsException("credentials must not be null"); } if (CommonHelper.isBlank(credentials.getToken())) { throw new CredentialsException("token must not be blank"); } final String token = credentials.getToken(); final CommonProfile profile = new CommonProfile(); profile.setId(token); credentials.setUserProfile(profile); }
@Override protected void internalInit() { CommonHelper.assertTrue( CommonHelper.isNotBlank(this.idpMetadata) || CommonHelper.isNotBlank(this.idpMetadataPath), "Either idpMetadata or idpMetadataPath must be provided"); CommonHelper.assertNotBlank("callbackUrl", this.callbackUrl); if (!this.callbackUrl.startsWith("http")) { throw new TechnicalException("SAML callbackUrl must be absolute"); } if (CommonHelper.isNotBlank(this.keystorePath) || CommonHelper.isNotBlank(this.keystorePassword) || CommonHelper.isNotBlank(this.privateKeyPassword)) { CommonHelper.assertNotBlank("keystorePath", this.keystorePath); CommonHelper.assertNotBlank("keystorePassword", this.keystorePassword); CommonHelper.assertNotBlank("privateKeyPassword", this.privateKeyPassword); // load private key from the keystore and provide it as OpenSAML credentials this.credentialProvider = new CredentialProvider(this.keystorePath, this.keystorePassword, this.privateKeyPassword); this.decrypter = new EncryptionProvider(this.credentialProvider).buildDecrypter(); } // Bootsrap OpenSAML try { DefaultBootstrap.bootstrap(); NamedKeyInfoGeneratorManager manager = Configuration.getGlobalSecurityConfiguration().getKeyInfoGeneratorManager(); X509KeyInfoGeneratorFactory generator = new X509KeyInfoGeneratorFactory(); generator.setEmitEntityCertificate(true); generator.setEmitEntityCertificateChain(true); manager.registerFactory(Saml2Client.SAML_METADATA_KEY_INFO_GENERATOR, generator); } catch (ConfigurationException e) { throw new SamlException("Error bootstrapping OpenSAML", e); } // required parserPool for XML processing final StaticBasicParserPool parserPool = newStaticBasicParserPool(); final AbstractMetadataProvider idpMetadataProvider = idpMetadataProvider(parserPool); final XMLObject md; try { md = idpMetadataProvider.getMetadata(); } catch (MetadataProviderException e) { throw new SamlException("Error initializing idpMetadataProvider", e); } // If no idpEntityId declared, select first EntityDescriptor entityId as our IDP entityId if (this.idpEntityId == null) { this.idpEntityId = getIdpEntityId(md); } // Generate our Service Provider metadata Saml2MetadataGenerator metadataGenerator = new Saml2MetadataGenerator(); if (this.credentialProvider != null) { metadataGenerator.setCredentialProvider(this.credentialProvider); metadataGenerator.setAuthnRequestSigned(true); } // If the spEntityId is blank, use the callback url if (CommonHelper.isBlank(this.spEntityId)) { this.spEntityId = getCallbackUrl(); } metadataGenerator.setEntityId(this.spEntityId); // Assertion consumer service url is the callback url metadataGenerator.setAssertionConsumerServiceUrl(getCallbackUrl()); // for now same for logout url metadataGenerator.setSingleLogoutServiceUrl(getCallbackUrl()); AbstractMetadataProvider spMetadataProvider = metadataGenerator.buildMetadataProvider(); // Initialize metadata provider for our SP and get the XML as a String try { spMetadataProvider.initialize(); this.spMetadata = metadataGenerator.printMetadata(); } catch (MetadataProviderException e) { throw new TechnicalException("Error initializing spMetadataProvider", e); } catch (MarshallingException e) { logger.warn("Unable to print SP metadata", e); } // Put IDP and SP metadata together ChainingMetadataProvider metadataManager = new ChainingMetadataProvider(); try { metadataManager.addMetadataProvider(idpMetadataProvider); metadataManager.addMetadataProvider(spMetadataProvider); } catch (MetadataProviderException e) { throw new TechnicalException("Error adding idp or sp metadatas to manager", e); } // Build the contextProvider this.contextProvider = new Saml2ContextProvider(metadataManager, this.idpEntityId, this.spEntityId); // Get an AuthnRequest builder this.authnRequestBuilder = new Saml2AuthnRequestBuilder(forceAuth, comparisonType, destinationBindingType); // Build the WebSSO handler for sending and receiving SAML2 messages MessageEncoder encoder = null; if (SAMLConstants.SAML2_POST_BINDING_URI.equals(destinationBindingType)) { // Get a velocity engine for the HTTP-POST binding (building of an HTML document) VelocityEngine velocityEngine = VelocityEngineFactory.getEngine(); encoder = new HTTPPostEncoder(velocityEngine, "/templates/saml2-post-binding.vm"); } else if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(destinationBindingType)) { encoder = new HTTPRedirectDeflateEncoder(); } else { throw new UnsupportedOperationException( "Binding type - " + destinationBindingType + " is not supported"); } // Do we need binding specific decoder? MessageDecoder decoder = new Pac4jHTTPPostDecoder(parserPool); this.handler = new Saml2WebSSOProfileHandler( this.credentialProvider, encoder, decoder, parserPool, destinationBindingType); // Build provider for digital signature validation and encryption this.signatureTrustEngineProvider = new SignatureTrustEngineProvider(metadataManager); // Build the SAML response validator this.responseValidator = new Saml2ResponseValidator(); if (this.maximumAuthenticationLifetime != null) { this.responseValidator.setMaximumAuthenticationLifetime(this.maximumAuthenticationLifetime); } }