Java tutorial
/* * Copyright 2012-2013 inBloom, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.slc.sli.api.resources.security; import java.io.IOException; import java.io.StringWriter; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.apache.commons.codec.binary.Base64; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.codehaus.jackson.map.ObjectMapper; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.opensaml.saml2.core.ArtifactResolve; import org.opensaml.saml2.core.ArtifactResponse; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Conditions; import org.opensaml.saml2.core.Subject; import org.opensaml.saml2.core.SubjectConfirmationData; import org.opensaml.ws.soap.soap11.Envelope; import org.opensaml.ws.soap.soap11.impl.EnvelopeImpl; import org.opensaml.xml.XMLObject; import org.slc.sli.api.security.context.APIAccessDeniedException; import org.slc.sli.api.security.roles.EdOrgContextualRoleBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.slc.sli.api.representation.CustomStatus; import org.slc.sli.api.security.OauthSessionManager; import org.slc.sli.api.security.SLIPrincipal; import org.slc.sli.api.security.SecurityEventBuilder; import org.slc.sli.api.security.service.AuditLogger; import org.slc.sli.api.security.context.resolver.RealmHelper; import org.slc.sli.api.security.resolve.RolesToRightsResolver; import org.slc.sli.api.security.resolve.UserLocator; import org.slc.sli.api.security.roles.Role; import org.slc.sli.api.security.saml.SamlAttributeTransformer; import org.slc.sli.api.security.saml.SamlHelper; import org.slc.sli.common.constants.EntityNames; import org.slc.sli.common.util.logging.LogLevelType; import org.slc.sli.common.util.logging.SecurityEvent; import org.slc.sli.common.util.tenantdb.TenantContext; import org.slc.sli.domain.Entity; import org.slc.sli.domain.NeutralCriteria; import org.slc.sli.domain.NeutralQuery; import org.slc.sli.domain.Repository; import org.w3c.dom.Document; /** * Process SAML assertions * * @author dkornishev */ @Component @Path("saml") public class SamlFederationResource { private static final Logger LOG = LoggerFactory.getLogger(SamlFederationResource.class); @Autowired private SamlHelper samlHelper; @Autowired private KeyStoreAccessor apiKeyStoreAccessor; @Autowired private ArtifactBindingHelper artifactBindingHelper; @Autowired private SOAPHelper soapHelper; @Autowired @Qualifier("validationRepo") private Repository<Entity> repo; @Autowired private UserLocator users; @Autowired private SamlAttributeTransformer transformer; @Autowired private RolesToRightsResolver resolver; @Autowired private OauthSessionManager sessionManager; @Value("${sli.security.sp.issuerName}") private String issuerName; @Value("${sli.api.cookieDomain}") private String apiCookieDomain; @Value("${sli.api.digital.signature.keyAlias}") String dsKeyStoreAlias; @Value("#{encryptor.decrypt('${sli.encryption.ldapKeyAlias}', '${sli.encryption.ldapKeyPass}', '${sli.api.digital.signature.keyPass}')}") String dsKeyStoreEntryPassword; @Value("${sli.api.client.certificate.keyAlias}") String clientCertKeyStoreAlias; @Value("#{encryptor.decrypt('${sli.encryption.ldapKeyAlias}', '${sli.encryption.ldapKeyPass}', '${sli.api.client.certificate.keyPass}')}") String clientCertKeyStoreEntryPassword; @Value("${sli.api.encryption.certificate.keyAlias}") String encryptionCertKeyStoreAlias; @Value("#{encryptor.decrypt('${sli.encryption.ldapKeyAlias}', '${sli.encryption.ldapKeyPass}', '${sli.api.encryption.certificate.keyPass}')}") String encryptionCertKeyStoreEntryPassword; @Autowired @Value("${sli.sandbox.enabled}") private boolean sandboxEnabled; @Autowired private RealmHelper realmHelper; @Context private HttpServletRequest httpServletRequest; private String metadata; private KeyStore.PrivateKeyEntry dsPKEntry; private KeyStore.PrivateKeyEntry clientCertPKEntry; private KeyStore.PrivateKeyEntry encryptPKEntry; @Autowired private SecurityEventBuilder securityEventBuilder; @Autowired private AuditLogger auditLogger; @Autowired private EdOrgContextualRoleBuilder edOrgRoleBuilder; public static SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd"); private static final String METADATA_TEMPLATE = "saml/samlMetadata-template.vm"; private static final String TEMPLATE_ISSUER_REFERENCE = "spIssuerName"; private static final String TEMPLATE_SIGNING_CERTIFICATE_REFERENCE = "signingCertificateText"; private static final String TEMPLATE_ENCRYPTION_CERTIFICATE_REFERENCE = "encryptionCertificateText"; @SuppressWarnings("unused") @PostConstruct private void processKeyStoreAndMetadata() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableEntryException { dsPKEntry = apiKeyStoreAccessor.getPrivateKeyEntry(dsKeyStoreAlias, dsKeyStoreEntryPassword); clientCertPKEntry = apiKeyStoreAccessor.getPrivateKeyEntry(clientCertKeyStoreAlias, clientCertKeyStoreEntryPassword); encryptPKEntry = apiKeyStoreAccessor.getPrivateKeyEntry(encryptionCertKeyStoreAlias, encryptionCertKeyStoreEntryPassword); StringWriter writer = new StringWriter(); Template veTemplate = getTemplate(); VelocityContext context = new VelocityContext(); context.put(TEMPLATE_ISSUER_REFERENCE, issuerName); context.put(TEMPLATE_SIGNING_CERTIFICATE_REFERENCE, fetchCertificateText(dsPKEntry)); context.put(TEMPLATE_ENCRYPTION_CERTIFICATE_REFERENCE, fetchCertificateText(encryptPKEntry)); veTemplate.merge(context, writer); metadata = writer.toString(); } /** * Get metadata describing saml federation. * This is an unsecured (public) resource. * * @return Response containing saml metadata */ @GET @Path("metadata") @Produces({ "text/xml" }) public Response getMetadata() { if (!metadata.isEmpty()) { return Response.ok(metadata).build(); } return Response.status(Response.Status.NOT_FOUND).build(); } /** * Handle IdP response when using POST Binding * @param postData * Response received from the IdP * @param uriInfo * Information about the URI * @return */ @POST @Path("sso/post") public Response processPostBinding(@FormParam("SAMLResponse") String postData, @Context UriInfo uriInfo) { LOG.info("Received a SAML post for SSO..."); TenantContext.setTenantId(null); if (postData == null || postData.isEmpty()) { throw new IllegalArgumentException("Empty SAML message"); } String decodedData = samlHelper.decodeSAMLPostResponse(postData); Document samlDocument = null; try { samlDocument = samlHelper.parseToDoc(decodedData); } catch (Exception e) { handleSAMLProcessingError(e, uriInfo.getRequestUri()); } org.opensaml.saml2.core.Response samlResponse = samlHelper .convertToSAMLResponse(samlDocument.getDocumentElement()); return processSAMLResponse(samlResponse, uriInfo); } /** * Handles artifact binding request. * @param request * @param uriInfo * @return */ @GET @Path("sso/artifact") public Response processArtifactBinding(@Context HttpServletRequest request, @Context UriInfo uriInfo) { String artifact = request.getParameter("SAMLart"); String realmId = request.getParameter("RelayState"); if (artifact == null) { throw new APIAccessDeniedException("No artifact provided by the IdP"); } String artifactUrl = samlHelper.getArtifactUrl(realmId, artifact); ArtifactResolve artifactResolve = artifactBindingHelper.generateArtifactResolveRequest(artifact, dsPKEntry, artifactUrl); Envelope soapEnvelope = artifactBindingHelper.generateSOAPEnvelope(artifactResolve); XMLObject response = soapHelper.sendSOAPCommunication(soapEnvelope, artifactUrl, clientCertPKEntry); ArtifactResponse artifactResponse = (ArtifactResponse) ((EnvelopeImpl) response).getBody() .getUnknownXMLObjects().get(0); org.opensaml.saml2.core.Response samlResponse = (org.opensaml.saml2.core.Response) artifactResponse .getMessage(); return processSAMLResponse(samlResponse, uriInfo); } protected Response processSAMLResponse(org.opensaml.saml2.core.Response samlResponse, UriInfo uriInfo) { if (!samlResponse.hasChildren()) { handleSAMLProcessingError(new APIAccessDeniedException("Empty SAML Response body"), uriInfo.getRequestUri()); } samlHelper.validateStatus(samlResponse); Assertion assertion = samlHelper.getAssertion(samlResponse, encryptPKEntry); samlHelper.validateSignature(samlResponse, assertion); String inResponseTo = samlResponse.getInResponseTo(); Entity session = sessionManager.getSessionForSamlId(inResponseTo); String requestedRealmId = (String) session.getBody().get("requestedRealmId"); Entity realm = getRealm(samlResponse.getIssuer().getValue(), requestedRealmId); String targetEdOrg = getTargetEdOrg(realm); Conditions conditions = assertion.getConditions(); if (conditions != null) { String notBefore = conditions.getNotBefore() == null ? null : conditions.getNotBefore().toString(); String notOnOrAfter = conditions.getNotOnOrAfter() == null ? null : conditions.getNotOnOrAfter().toString(); validateTimeRange("SAML Conditions failed.", notBefore, notOnOrAfter, targetEdOrg); } Subject subject = assertion.getSubject(); if (subject != null) { SubjectConfirmationData subjectConfirmationData = subject.getSubjectConfirmations().get(0) .getSubjectConfirmationData(); String notBefore = subjectConfirmationData.getNotBefore() == null ? null : subjectConfirmationData.getNotBefore().toString(); String notOnOrAfter = subjectConfirmationData.getNotOnOrAfter() == null ? null : subjectConfirmationData.getNotOnOrAfter().toString(); validateSubject(uriInfo.getAbsolutePath(), subjectConfirmationData.getRecipient(), notBefore, notOnOrAfter, targetEdOrg); } else { raiseSamlValidationError("SAML response is missing Subject.", targetEdOrg); } LinkedMultiValueMap<String, String> attributes = samlHelper.extractAttributesFromResponse(assertion); return authenticateUser(attributes, realm, targetEdOrg, inResponseTo, session, uriInfo.getRequestUri()); } private void handleSAMLProcessingError(Exception e, URI uri) { SecurityEvent event = securityEventBuilder.createSecurityEvent(this.getClass().getName(), uri, "", false); try { event.setExecutedOn(InetAddress.getLocalHost().getHostName()); } catch (UnknownHostException ue) { LOG.info("Could not find hostname for security event logging!"); } if (httpServletRequest != null) { event.setUserOrigin(httpServletRequest.getRemoteHost()); event.setAppId(httpServletRequest.getHeader("User-Agent")); event.setUser(httpServletRequest.getRemoteUser()); // The origin header contains the URI info of the IDP server that sends the SAML data. event.setLogMessage( "SAML message received from " + httpServletRequest.getHeader("Origin") + " is invalid!"); event.setLogLevel(LogLevelType.TYPE_WARN); } else { event.setLogMessage("HttpServletRequest is missing, and this should never happen!!"); event.setLogLevel(LogLevelType.TYPE_ERROR); } auditLogger.audit(event); raiseSamlValidationError(e.getMessage(), null); } private void raiseSamlValidationError(String message, String targetEdOrg) { LOG.error(message); throw new APIAccessDeniedException("Authorization could not be verified.", targetEdOrg); } private Entity getRealm(String issuer, String realmId) { NeutralQuery neutralQuery = new NeutralQuery(); neutralQuery.setOffset(0); neutralQuery.setLimit(1); neutralQuery.addCriteria(new NeutralCriteria("idp.id", "=", issuer)); neutralQuery.addCriteria(new NeutralCriteria("_id", "=", realmId)); Entity realm = repo.findOne("realm", neutralQuery); if (realm == null) { raiseSamlValidationError("Invalid realm: " + issuer, null); } return realm; } private String getTargetEdOrg(Entity realm) { String targetEdOrg = ""; if (realm.getBody() != null) { targetEdOrg = (String) realm.getBody().get("edOrg"); } return targetEdOrg; } private void validateTimeRange(String errorMessagePrefix, String notBefore, String notOnOrAfter, String targetEdOrg) { if (!isTimeInRange(notBefore, notOnOrAfter)) { raiseSamlValidationError( errorMessagePrefix + " Current time not in range " + notBefore + " to " + notOnOrAfter + ".", targetEdOrg); } } private void validateSubject(URI uri, String recipient, String notBefore, String notOnOrAfter, String targetEdOrg) { if (!uri.toString().equals(recipient)) { raiseSamlValidationError("SAML Recipient was invalid, was " + recipient, targetEdOrg); } validateTimeRange("SAML Subject failed.", notBefore, notOnOrAfter, targetEdOrg); } /** * Authenticate SSO user. * * @param attributes - SAML assertion attributes * @param realm - User's realm * @param targetEdOrg - Target edOrg name * @param inResponseTo - Assertion recipient * @param session - User session * @param requestUri - Request URI * * @return - SAML response from API */ protected Response authenticateUser(LinkedMultiValueMap<String, String> rawAttributes, Entity realm, String targetEdOrg, String inResponseTo, Entity session, URI requestUri) { // Apply attribute transforms. LinkedMultiValueMap<String, String> attributes = transformer.apply(realm, rawAttributes); String tenant = null; String sandboxTenant = null; // For developers coming from developer realm. String realmTenant = (String) realm.getBody().get("tenantId"); String samlTenant = attributes.getFirst("tenant"); Boolean isAdminRealm = (Boolean) realm.getBody().get("admin"); isAdminRealm = (isAdminRealm != null) ? isAdminRealm : Boolean.FALSE; Boolean isDevRealm = (Boolean) realm.getBody().get("developer"); isDevRealm = (isDevRealm != null) ? isDevRealm : Boolean.FALSE; if (isAdminRealm && sandboxEnabled) { // Sandbox mode using the SimpleIDP. // Reset isAdminRealm based on the value of the saml isAdmin attribute, since this same realm is used for impersonation and admin logins. isAdminRealm = Boolean.valueOf(attributes.getFirst("isAdmin")); // Accept the tenantId from the Sandbox-IDP, or if none then it's an admin user. if (isAdminRealm) { tenant = null; // Operator admin. } else { // Impersonation login; require tenant in the saml. if (samlTenant != null) { tenant = samlTenant; } else { LOG.error( "Attempted login by a user in sandbox mode but no tenant was specified in the saml message."); throw new APIAccessDeniedException("Invalid user configuration.", (String) realm.getBody().get("edOrg")); } } } else if (isAdminRealm) { // Prod mode, admin login. tenant = null; } else if (isDevRealm) { // Prod mode, developer login. tenant = null; sandboxTenant = samlTenant; samlTenant = null; } else { // Regular IDP login. tenant = realmTenant; } SLIPrincipal principal = createPrincipal(isAdminRealm, tenant, attributes, isDevRealm, targetEdOrg, realm, samlTenant, sandboxTenant); Map<String, Object> appSession = sessionManager.getAppSession(inResponseTo, session); String authorizationCode = (String) ((Map<String, Object>) appSession.get("code")).get("value"); auditSuccessfulLogin(principal, session, requestUri, authorizationCode, realm, appSession, tenant); return getLoginResponse(appSession, authorizationCode, requestUri, session.getEntityId()); } /** * Create an SLIPrincipal instance for the user's login session. * * @param isAdminRealm - Indicates if user is using admin realm or not * @param tenant - User's tenant ID * @param attributes - SAML assertion attributes * @param isDevRealm - Indicates if user is using developer realm or not * @param targetEdOrg - Target edOrg name * @param realm - User's realm * @param samlTenant - User's SAML tenant ID * @param sandboxTenant - User's sandbox tenant ID * * @return - Newly created SLIPrincipal instance */ protected SLIPrincipal createPrincipal(boolean isAdminRealm, String tenant, LinkedMultiValueMap<String, String> attributes, boolean isDevRealm, String targetEdOrg, Entity realm, String samlTenant, String sandboxTenant) { LOG.debug("Authenticating user is an admin: " + isAdminRealm); SLIPrincipal principal = users.locate(tenant, attributes.getFirst("userId"), attributes.getFirst("userType")); String userName = getUserNameFromEntity(principal.getEntity()); if (userName != null) { principal.setName(userName); } else { if (isAdminRealm || isDevRealm) { principal.setFirstName(attributes.getFirst("givenName")); principal.setLastName(attributes.getFirst("sn")); principal.setVendor(attributes.getFirst("vendor")); principal.setEmail(attributes.getFirst("mail")); } principal.setName(attributes.getFirst("userName")); } // Cache realm edOrg for security events. principal.setRealmEdOrg(targetEdOrg); principal.setRealm(realm.getEntityId()); principal.setEdOrg(attributes.getFirst("edOrg")); principal.setAdminRealm(attributes.getFirst("edOrg")); if (SLIPrincipal.NULL_ENTITY_ID.equals(principal.getEntity().getEntityId()) && !(isAdminRealm || isDevRealm)) { // If we couldn't find an Entity for the user and this isn't an admin realm, then we have no valid user. throw new APIAccessDeniedException("Invalid user.", realm); } if (!(isAdminRealm || isDevRealm) && !realmHelper.isUserAllowedLoginToRealm(principal.getEntity(), realm)) { throw new APIAccessDeniedException("User is not associated with realm.", realm); } setPrincipalRoles(principal, attributes, realm, tenant, isAdminRealm, isDevRealm); if (samlTenant != null) { principal.setTenantId(samlTenant); TenantContext.setTenantId(samlTenant); TenantContext.setIsSystemCall(false); NeutralQuery idQuery = new NeutralQuery(); idQuery.addCriteria(new NeutralCriteria("stateOrganizationId", NeutralCriteria.OPERATOR_EQUAL, principal.getEdOrg())); Entity edOrg = repo.findOne(EntityNames.EDUCATION_ORGANIZATION, idQuery); if (edOrg != null) { principal.setEdOrgId(edOrg.getEntityId()); } else { LOG.debug("Failed to find edOrg with stateOrganizationID {} and tenantId {}", principal.getEdOrg(), principal.getTenantId()); } } if ((sandboxTenant != null) && isDevRealm) { principal.setSandboxTenant(sandboxTenant); } return principal; } private void setPrincipalRoles(SLIPrincipal principal, LinkedMultiValueMap<String, String> attributes, Entity realm, String tenant, boolean isAdminRealm, boolean isDevRealm) { List<String> roles = attributes.get("roles"); if (roles == null || roles.isEmpty()) { LOG.error("Attempted login by a user that did not include any roles in the SAML Assertion."); throw new APIAccessDeniedException("Invalid user. No roles specified for user.", realm); } Set<Role> sliRoleSet = resolver.mapRoles(tenant, realm.getEntityId(), roles, isAdminRealm); List<String> sliRoleList = new ArrayList<String>(); boolean isAdminUser = true; for (Role role : sliRoleSet) { sliRoleList.addAll(role.getName()); if (!role.isAdmin()) { isAdminUser = false; break; } } if (!(isAdminRealm || isDevRealm) && (principal.getUserType() == null || principal.getUserType().equals("") || principal.getUserType().equals(EntityNames.STAFF))) { Map<String, List<String>> sliEdOrgRoleMap = edOrgRoleBuilder.buildValidStaffRoles(realm.getEntityId(), principal.getEntity().getEntityId(), tenant, roles); principal.setEdOrgRoles(sliEdOrgRoleMap); Set<String> allRoles = new HashSet<String>(); for (List<String> roleList : sliEdOrgRoleMap.values()) { allRoles.addAll(roleList); } principal.setRoles(new ArrayList<String>(allRoles)); } else { principal.setRoles(sliRoleList); if (principal.getRoles().isEmpty()) { LOG.debug( "Attempted login by a user that included no roles in the SAML Assertion that mapped to any of the SLI roles."); throw new APIAccessDeniedException( "Invalid user. No valid role mappings exist for the roles specified in the SAML Assertion.", realm); } } principal.setAdminUser(isAdminUser); principal.setAdminRealmAuthenticated(isAdminRealm || isDevRealm); } private String getUserNameFromEntity(Entity entity) { if (entity != null) { @SuppressWarnings("rawtypes") Map nameMap = (Map) entity.getBody().get("name"); if (nameMap != null) { StringBuffer name = new StringBuffer(); if (nameMap.containsKey("personalTitlePrefix")) { name.append((String) nameMap.get("personalTitlePrefix")); name.append(" "); } name.append((String) nameMap.get("firstName")); name.append(" "); name.append((String) nameMap.get("lastSurname")); if (nameMap.containsKey("generationCodeSuffix")) { name.append(" "); name.append((String) nameMap.get("generationCodeSuffix")); } return name.toString(); } } return null; } private void auditSuccessfulLogin(SLIPrincipal principal, Entity session, URI requestUri, String authorizationCode, Entity realm, Map<String, Object> appSession, String tenant) { TenantContext.setIsSystemCall(true); ObjectMapper jsoner = new ObjectMapper(); Map<String, Object> mapForm = jsoner.convertValue(principal, Map.class); mapForm.remove("entity"); if (!mapForm.containsKey("userType")) { mapForm.put("userType", EntityNames.STAFF); } session.getBody().put("principal", mapForm); sessionManager.updateSession(session); SecurityEvent successfulLogin = securityEventBuilder.createSecurityEvent(this.getClass().getName(), requestUri, "", principal, null, realm, null, true); successfulLogin.setOrigin(httpServletRequest.getRemoteHost() + ":" + httpServletRequest.getRemotePort()); successfulLogin.setCredential(authorizationCode); successfulLogin .setUserOrigin(httpServletRequest.getRemoteHost() + ":" + httpServletRequest.getRemotePort()); successfulLogin.setLogLevel(LogLevelType.TYPE_INFO); successfulLogin.setRoles(principal.getRoles()); String applicationDetails = null; if (appSession != null) { String clientId = (String) appSession.get("clientId"); if (clientId != null) { NeutralQuery appQuery = new NeutralQuery(); appQuery.setOffset(0); appQuery.setLimit(1); appQuery.addCriteria(new NeutralCriteria("client_id", "=", clientId)); Entity application = repo.findOne("application", appQuery); if (application != null) { Map<String, Object> body = application.getBody(); if (body != null) { String name = (String) body.get("name"); String createdBy = (String) body.get("created_by"); successfulLogin.setAppId(name + "," + clientId); applicationDetails = String.format("%s by %s", name, createdBy); } } } } successfulLogin.setUser(principal.getExternalId()); successfulLogin.setLogMessage(principal.getExternalId() + " from tenant " + tenant + " logged successfully into " + applicationDetails + "."); auditLogger.audit(successfulLogin); } private Response getLoginResponse(Map<String, Object> appSession, String authorizationCode, URI requestUri, String sessionId) { Object state = appSession.get("state"); Boolean isInstalled = (Boolean) appSession.get("installed"); if (isInstalled) { Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put("authorization_code", authorizationCode); if (state != null) { resultMap.put("state", state); } LOG.info("Sending back authorization token for installed app: {}", authorizationCode); return Response.ok(resultMap).build(); } else { String redirectUri = (String) appSession.get("redirectUri"); UriBuilder builder = UriBuilder.fromUri(redirectUri); builder.queryParam("code", authorizationCode); if (state != null) { builder.queryParam("state", state); } boolean runningSsl = requestUri.getScheme().equals("https"); URI redirect = builder.build(); return Response.status(CustomStatus.FOUND) .cookie(new NewCookie("_tla", sessionId, "/", apiCookieDomain, "", -1, runningSsl)) .location(redirect).build(); } } private String fetchCertificateText(KeyStore.PrivateKeyEntry keystoreEntry) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException { X509Certificate certificate = (X509Certificate) keystoreEntry.getCertificate(); Base64 encoder = new Base64(64); String certificateText = new String(encoder.encode(certificate.getEncoded())); return certificateText; } /** * Check that the current time is within the specified range. * * @param notBefore * - can be null to skip before check * @param notOnOrAfter * - can be null to skip after check * * @return true if in range, false otherwise */ private boolean isTimeInRange(String notBefore, String notOnOrAfter) { DateTime currentTime = new DateTime(DateTimeZone.UTC); if (notBefore != null) { DateTime calNotBefore = DateTime.parse(notBefore); if (currentTime.isBefore(calNotBefore)) { LOG.debug("{} is before {}.", currentTime, calNotBefore); return false; } } if (notOnOrAfter != null) { DateTime calNotOnOrAfter = DateTime.parse(notOnOrAfter); if (currentTime.isAfter(calNotOnOrAfter) || currentTime.isEqual(calNotOnOrAfter)) { LOG.debug("{} is on or after {}.", currentTime, calNotOnOrAfter); return false; } } return true; } private Template getTemplate() { Properties props = new Properties(); props.setProperty("resource.loader", "class"); props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); VelocityEngine velocityEngine = new VelocityEngine(props); velocityEngine.init(); return velocityEngine.getTemplate(METADATA_TEMPLATE); } }