Java tutorial
/** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. * If a copy of the MPL was not distributed with this file, You can obtain one at * http://mozilla.org/MPL/2.0/. * * This Source Code Form is also subject to the terms of the Health-Related Additional * Disclaimer of Warranty and Limitation of Liability available at * http://www.carewebframework.org/licensing/disclaimer. */ package org.carewebframework.security.spring; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.lang.CharEncoding; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.carewebframework.api.alias.AliasType; import org.carewebframework.api.alias.AliasTypeRegistry; import org.carewebframework.api.context.ContextManager; import org.carewebframework.api.context.IContextManager; import org.carewebframework.api.domain.IUser; import org.carewebframework.api.security.ISecurityDomain; import org.carewebframework.api.security.ISecurityService; import org.carewebframework.api.security.SecurityUtil; import org.carewebframework.ui.Application; import org.carewebframework.ui.FrameworkWebSupport; import org.carewebframework.ui.zk.PopupDialog; import org.carewebframework.ui.zk.PromptDialog; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.ObjectUtils; import org.zkoss.util.resource.Labels; import org.zkoss.zk.ui.Desktop; /** * Base Spring Security implementation. */ public abstract class AbstractSecurityService implements ISecurityService { private static final Log log = LogFactory.getLog(AbstractSecurityService.class); private String logoutTarget; private String passwordChangeUrl; private final AliasType authorityAlias = AliasTypeRegistry.getType(ALIAS_TYPE_AUTHORITY); private final Map<String, ISecurityDomain> securityDomains = new LinkedHashMap<String, ISecurityDomain>(); private volatile boolean initialized; /** * Returns Spring security Authentication object via * <code>SpringContextHolder.getContext().getAuthentication()</code>. * * @return Authentication or null if no authentication information is found */ public static Authentication getAuthentication() { return SecurityContextHolder.getContext().getAuthentication(); } /** * Sets cookies for logout. * * @param target Target url after successful login. * @param message Message to display in logout dialog. */ protected static void setLogoutAttributes(final String target, final String message) { setCookie(Constants.LOGOUT_WARNING_ATTR, message, "Application logged out."); setCookie(Constants.LOGOUT_TARGET_ATTR, target, "/"); } /** * Sets the cookie in the http response. * * @param cookieName Name of the cookie. * @param value Value of the cookie which will be base64-encoded. * @param deflt Default value if value is null. */ private static void setCookie(final String cookieName, final String value, final String deflt) { FrameworkWebSupport.setCookie(cookieName, (value == null ? deflt : value)); } /** * Gets the specified logout attribute value. The value is obtained from a cookie which is then * deleted. * * @param attributeName Name of the logout attribute. * @param deflt Default value to return if none found. * @return Value of the attribute, which is automatically converted from its base64 encoding. */ public static String getLogoutAttribute(final String attributeName, final String deflt) { final String value = FrameworkWebSupport.getCookieValue(attributeName); //delete cookie FrameworkWebSupport.setCookie(attributeName, null); return StringUtils.isEmpty(value) ? deflt : value; } /** * Logout out the current desktop instance. * * @param force If true, force logout without user interaction. * @param target Optional target url for next login. * @param message Optional message to indicate reason for logout. * @return True if operation was successful. */ @Override public boolean logout(final boolean force, String target, final String message) { log.trace("Logging Out"); final IContextManager contextManager = ContextManager.getInstance(); final boolean result = contextManager == null || contextManager.reset(force) || force; if (result) { if (target == null) { try { target = FrameworkWebSupport.addQueryString(FrameworkWebSupport.getRequestUrl(), FrameworkWebSupport.getRequestParams()); } catch (final Exception e) { } } setLogoutAttributes(target, message); final Desktop contextDesktop = FrameworkWebSupport.getDesktop(); log.debug("Redirecting Desktop to logout filter URI: " + contextDesktop); String queryParam = replaceParam(replaceParam(logoutTarget, "%target%", target), "%message%", message); contextDesktop.getExecution().sendRedirect(Constants.LOGOUT_URI + queryParam); Application.getInstance().register(contextDesktop, false); } return result; } /** * Replaces the inline parameter with the specified value. * * @param text Text containing parameter placeholder. * @param param Parameter name. * @param value Value to replace (will be url-encoded). * @return Updated text. */ private String replaceParam(String text, String param, String value) { if (text.contains(param)) { try { value = value == null ? "" : URLEncoder.encode(value, CharEncoding.UTF_8); text = text.replace(param, value); } catch (UnsupportedEncodingException e) { log.error("Error encoding parameter value.", e); } } return text; } /** * Register an alias for an authority. * * @param authority String representation of an authority. * @param alias String representation of an authority alias. If null, removes an existing alias. */ @Override public void setAuthorityAlias(String authority, String alias) { authorityAlias.register(authority, alias); } /** * Returns whether the current context has authenticated * * @return boolean true if Authentication token is found and is not an Anonymous User */ @Override public boolean isAuthenticated() { Authentication auth = getAuthentication(); if (auth == null) { return false; } Object principal = auth.getPrincipal(); String username = principal instanceof String ? (String) principal : ((org.springframework.security.core.userdetails.User) principal).getUsername(); return (username != null && !username.equals(Constants.ANONYMOUS_USER)); } /** * Returns the authenticated user object from the current security context. * * @return The authenticated user object, or null if none present. */ @Override public IUser getAuthenticatedUser() { Authentication authentication = getAuthentication(); Object details = authentication == null ? null : authentication.getDetails(); return (details instanceof CWFAuthenticationDetails) ? (IUser) ((CWFAuthenticationDetails) details).getDetail("user") : null; } /** * Returns the security domain that authenticated the user. * * @return The security domain. */ @Override public ISecurityDomain getAuthenticatingDomain() { IUser user = getAuthenticatedUser(); return user == null ? null : user.getSecurityDomain(); } /** * <p> * Returns true if the Authentication object is granted debug privilege (determined by the role * {@link Constants#PRIV_DEBUG}) * </p> * * @return boolean true if authenticated principal is granted a verbose view */ @Override public boolean hasDebugRole() { return isGranted(Constants.PRIV_DEBUG); } /** * <p> * Returns true if the Authentication object has the specified <code>grantedAuthority</code> * </p> * <p> * <i>Note:</i>Privileges are prefixed with "PRIV_" and roles are prefixed with "ROLE_" * </p> * * @param grantedAuthority String representation of an authority * @return boolean true if found */ @Override public boolean isGranted(String grantedAuthority) { return isGranted(grantedAuthority, getAuthentication()); } /** * Checks the current SecurityContext for the specified authorities. * * @param grantedAuthorities Comma-delimited string of granted authorities * @param checkAllRoles boolean true-specified roles must be found in security context * authorities, false-security context must contain at least 1 specified authority * @return True if Authentication is granted authorities */ @Override public boolean isGranted(String grantedAuthorities, boolean checkAllRoles) { Authentication authentication = getAuthentication(); if (authentication == null) { log.info("Authentication context was null during check for granted authorities '" + ObjectUtils.nullSafeToString(grantedAuthorities) + "'."); return false; } if (grantedAuthorities == null) { return false; } for (String desiredAuthority : grantedAuthorities.split(",")) { if (!desiredAuthority.isEmpty()) { if (isGranted(desiredAuthority, authentication) != checkAllRoles) { return !checkAllRoles; } } } return checkAllRoles; } /** * Determine if the granted authority exists within the authentication context. * * @param grantedAuthority The granted authority to check. * @param authentication The authentication context. * @return True if the granted authority exists within the authentication context. */ private boolean isGranted(String grantedAuthority, Authentication authentication) { if (authentication == null) { log.info("Authentication context was null during check for granted authority '" + grantedAuthority + "'."); return false; } boolean result = authentication.getAuthorities().contains(new SimpleGrantedAuthority(grantedAuthority)); if (!result) { String alias = authorityAlias.get(grantedAuthority); return alias != null && isGranted(alias, authentication); } return result; } /** * Override to implement login restrictions. */ @Override public String loginDisabled() { return null; } /** * Returns the logout target url. * * @return Logout target url. */ public String getLogoutTarget() { return logoutTarget; } /** * Sets the logout target url. * * @param logoutTarget Logout target url. */ public void setLogoutTarget(String logoutTarget) { this.logoutTarget = logoutTarget; } /** * Sets the url of the password change dialog. * * @param passwordChangeUrl Url of the password change dialog. */ public void setPasswordChangeUrl(String passwordChangeUrl) { this.passwordChangeUrl = passwordChangeUrl; } /** * @see org.carewebframework.api.security.ISecurityService#changePassword() */ @Override public void changePassword() { if (canChangePassword()) { if (PopupDialog.popup(passwordChangeUrl, false, false) == null) { PromptDialog.showError(Labels.getLabel("password.change.dialog.unavailable")); } } else { PromptDialog.showWarning(Labels.getLabel(Constants.LBL_PASSWORD_CHANGE_UNAVAILABLE)); } } /** * @see org.carewebframework.api.security.ISecurityService#canChangePassword() */ @Override public boolean canChangePassword() { return passwordChangeUrl != null; } /** * Generates a new random password Length of password dictated by * {@link Constants#LBL_PASSWORD_RANDOM_LENGTH} and * {@link Constants#LBL_PASSWORD_RANDOM_CONSTRAINTS} * * @return String The generated password */ @Override public String generateRandomPassword() { int len = getRandomPasswordLength(); return SecurityUtil.generateRandomPassword(len, len, Labels.getLabel(Constants.LBL_PASSWORD_RANDOM_CONSTRAINTS).split("\n")); } /** * Returns the minimum length for random password. * * @return Minimum length for random password. */ protected int getRandomPasswordLength() { return NumberUtils.toInt(Labels.getLabel(Constants.LBL_PASSWORD_RANDOM_LENGTH), 12); } /** * @see org.carewebframework.api.security.ISecurityService#getSecurityDomains() */ @Override public Collection<ISecurityDomain> getSecurityDomains() { doInit(); return Collections.unmodifiableCollection(securityDomains.values()); } /** * @see org.carewebframework.api.security.ISecurityService#getSecurityDomain */ @Override public ISecurityDomain getSecurityDomain(String logicalId) { doInit(); return securityDomains.get(logicalId); } /** * Registers a security domain. * * @param securityDomain Security domain to register. */ protected void registerSecurityDomain(ISecurityDomain securityDomain) { securityDomains.put(securityDomain.getLogicalId(), securityDomain); } /** * Registers a collection of security domains. * * @param securityDomains Security domains to register. */ protected void registerSecurityDomains(Collection<ISecurityDomain> securityDomains) { for (ISecurityDomain securityDomain : securityDomains) { registerSecurityDomain(securityDomain); } } /** * Override to provide just-in-time loading of security domains. */ protected void initSecurityDomains() { } /** * Initialize domains. */ private void doInit() { if (!initialized) { synchronized (securityDomains) { if (!initialized) { initSecurityDomains(); initialized = true; } } } } }