/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.validation; import static org.geoserver.security.validation.SecurityConfigException.*; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.security.GeoServerAuthenticationProvider; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerSecurityFilterChain; import org.geoserver.security.GeoServerSecurityFilterChainProxy; import org.geoserver.security.GeoServerSecurityManager; import org.geoserver.security.GeoServerSecurityProvider; import org.geoserver.security.GeoServerUserGroupService; import org.geoserver.security.HtmlLoginFilterChain; import org.geoserver.security.MasterPasswordProvider; import org.geoserver.security.RequestFilterChain; import org.geoserver.security.ServiceLoginFilterChain; import org.geoserver.security.VariableFilterChain; import org.geoserver.security.config.PasswordPolicyConfig; import org.geoserver.security.config.SecurityAuthProviderConfig; import org.geoserver.security.config.SecurityManagerConfig; import org.geoserver.security.config.SecurityNamedServiceConfig; import org.geoserver.security.config.SecurityRoleServiceConfig; import org.geoserver.security.config.SecurityUserGroupServiceConfig; import org.geoserver.security.filter.GeoServerAuthenticationFilter; import org.geoserver.security.filter.GeoServerSecurityFilter; import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.password.GeoServerPasswordEncoder; import org.geoserver.security.password.MasterPasswordProviderConfig; import org.geoserver.security.password.PasswordValidator; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.util.StringUtils; public class SecurityConfigValidator extends AbstractSecurityValidator{ public SecurityConfigValidator(GeoServerSecurityManager securityManager) { super(securityManager); } /** * Get the proper {@link SecurityConfigValidator} object * * @param serviceClass * @param className * */ static public SecurityConfigValidator getConfigurationValiator(Class <?> serviceClass, String className) throws SecurityConfigException { GeoServerSecurityProvider prov = GeoServerSecurityProvider.getProvider(serviceClass, className); if (className == null) throw new SecurityConfigException(CLASSNAME_REQUIRED,new Object[]{}); //TODO: remove the call to extensions, have teh security manager be passed in return prov.createConfigurationValidator(GeoServerExtensions.bean(GeoServerSecurityManager.class)); } /** * Checks the {@link SecurityManagerConfig} object * * @param config * @param oldConfig * @throws SecurityConfigException */ public void validateManagerConfig(SecurityManagerConfig config, SecurityManagerConfig oldConfig) throws SecurityConfigException{ String encrypterName =config.getConfigPasswordEncrypterName(); if (isNotEmpty(encrypterName)==false) { throw createSecurityException(PASSWORD_ENCODER_REQUIRED); } GeoServerPasswordEncoder encoder = null; try { encoder = manager.loadPasswordEncoder(config.getConfigPasswordEncrypterName()); } catch (NoSuchBeanDefinitionException ex) { throw createSecurityException(INVALID_PASSWORD_ENCODER_$1, encrypterName); } if (encoder == null) { throw createSecurityException(INVALID_PASSWORD_ENCODER_$1, encrypterName); } if (!encoder.isReversible()) { throw createSecurityException(INVALID_PASSWORD_ENCODER_$1, encrypterName); } if (!manager.isStrongEncryptionAvailable()) { if (encoder!=null && encoder.isAvailableWithoutStrongCryptogaphy()==false) { throw createSecurityException(INVALID_STRONG_CONFIG_PASSWORD_ENCODER); } } String roleServiceName = config.getRoleServiceName(); if (roleServiceName==null) roleServiceName=""; try { if (manager.listRoleServices().contains(roleServiceName)==false) throw createSecurityException(ROLE_SERVICE_NOT_FOUND_$1, roleServiceName); } catch (IOException e) { throw new RuntimeException(e); } SortedSet<String> authProviders=null; try{ authProviders =manager.listAuthenticationProviders(); } catch (IOException e) { throw new RuntimeException(e); } for (String authProvName : config.getAuthProviderNames()) { if (authProviders.contains(authProvName)==false) throw createSecurityException(AUTH_PROVIDER_NOT_FOUND_$1, authProvName); } // check the filter chain GeoServerSecurityFilterChain chain = config.getFilterChain(); GeoServerSecurityFilterChain oldChain = oldConfig.getFilterChain(); if (chain == null) { throw createSecurityException(SecurityConfigException.FILTER_CHAIN_NULL_ERROR); } // check for remove for (RequestFilterChain oldRequestChain : oldChain.getRequestChains()) { if (chain.getRequestChainByName(oldRequestChain.getName())==null) { if (oldRequestChain.canBeRemoved()==false) { throw createSecurityException( SecurityConfigException.FILTER_CHAIN_NOT_REMOVEABLE_$1,oldRequestChain.getName()); } } } // check for unique chain names for (RequestFilterChain requestChain : chain.getRequestChains()) { Set<String> chainNames = new HashSet<String> (); // valid name if (isNotEmpty(requestChain.getName())==false) { throw createSecurityException(SecurityConfigException.FILTER_CHAIN_NAME_MANDATORY); } if (chainNames.contains(requestChain.getName())) { throw createSecurityException( SecurityConfigException.FILTER_CHAIN_NAME_NOT_UNIQUE_$1,requestChain.getName()); } chainNames.add(requestChain.getName()); } for (RequestFilterChain requestChain : chain.getRequestChains()) { validateRequestFilterChain(requestChain); } } public void validateRequestFilterChain(RequestFilterChain requestChain) throws SecurityConfigException { if (isNotEmpty(requestChain.getName())==false) { throw createSecurityException(SecurityConfigException.FILTER_CHAIN_NAME_MANDATORY); } if (requestChain.getPatterns().isEmpty()) { throw createSecurityException(SecurityConfigException.PATTERN_LIST_EMPTY_$1,requestChain.getName()); } GeoServerSecurityFilterChainProxy proxy = GeoServerExtensions.bean(GeoServerSecurityFilterChainProxy.class); String roleFilterName = requestChain.getRoleFilterName(); if (StringUtils.hasLength(roleFilterName)) { try { if (proxy.lookupFilter(roleFilterName)==null) { throw createSecurityException(SecurityConfigException.UNKNOWN_ROLE_FILTER_$2,requestChain.getName(),roleFilterName); } } catch (IOException ex) { throw new RuntimeException(ex); } } if (requestChain instanceof VariableFilterChain) { if (requestChain.isDisabled()==false && requestChain.getFilterNames().isEmpty()) throw createSecurityException(SecurityConfigException.FILTER_CHAIN_EMPTY_$1,requestChain.getName()); String interceptorFilterName = ((VariableFilterChain) requestChain).getInterceptorName(); if (StringUtils.hasLength(interceptorFilterName)) { try { if (proxy.lookupFilter(interceptorFilterName)==null) { throw createSecurityException(SecurityConfigException.UNKNOWN_INTERCEPTOR_FILTER_$2,requestChain.getName(),interceptorFilterName); } } catch (IOException ex) { throw new RuntimeException(ex); } } else { throw createSecurityException(SecurityConfigException.INTERCEPTOR_FILTER_MANDATORY_$1,requestChain.getName()); } String exceptionTranslationName = ((VariableFilterChain) requestChain).getExceptionTranslationName(); if (StringUtils.hasLength(exceptionTranslationName)) { try { if (proxy.lookupFilter(exceptionTranslationName)==null) { throw createSecurityException(SecurityConfigException.UNKNOWN_EXCEPTION_FILTER_$2,requestChain.getName(),exceptionTranslationName); } } catch (IOException ex) { throw new RuntimeException(ex); } } else { throw createSecurityException(SecurityConfigException.EXCEPTION_FILTER_MANDATORY_$1,requestChain.getName()); } int index = requestChain.getFilterNames().indexOf(GeoServerSecurityFilterChain.ANONYMOUS_FILTER); if (index!=-1 && index != requestChain.getFilterNames().size()-1) throw createSecurityException(SecurityConfigException.ANONYMOUS_NOT_LAST_$1,requestChain.getName()); for (String filterName : requestChain.getFilterNames()) { GeoServerSecurityFilter filter=null; try { filter = (GeoServerSecurityFilter)proxy.lookupFilter(filterName); } catch (IOException ex) { throw new RuntimeException(ex); } if (filter==null) throw createSecurityException(SecurityConfigException.UNKNOWN_FILTER_$2,requestChain.getName(),filterName); if (filter instanceof GeoServerAuthenticationFilter == false) throw createSecurityException(SecurityConfigException.NOT_AN_AUTHENTICATION_FILTER_$2,requestChain.getName(),filterName); GeoServerAuthenticationFilter authFilter = (GeoServerAuthenticationFilter) filter; if (requestChain instanceof HtmlLoginFilterChain && authFilter.applicableForHtml()==false) { throw createSecurityException(SecurityConfigException.NOT_A_HTML_AUTHENTICATION_FILTER_$2,requestChain.getName(),filterName); } if (requestChain instanceof ServiceLoginFilterChain && authFilter.applicableForServices()==false) { throw createSecurityException(SecurityConfigException.NOT_A_SERVICE_AUTHENTICATION_FILTER_$2,requestChain.getName(),filterName); } } } } protected void checkExtensionPont(Class<?> extensionPoint, String className) throws SecurityConfigException{ if (isNotEmpty(className)==false) { throw createSecurityException(CLASSNAME_REQUIRED); } Class<?> aClass = null; try { aClass=Class.forName(className); } catch (ClassNotFoundException e) { throw createSecurityException(CLASS_NOT_FOUND_$1, className); } if (extensionPoint.isAssignableFrom(aClass)==false) { throw createSecurityException(CLASS_WRONG_TYPE_$2, extensionPoint, className); } } protected void checkServiceName(Class<?> extensionPoint,String name) throws SecurityConfigException{ if (name==null || name.isEmpty()) throw createSecurityException(NAME_REQUIRED); } protected SortedSet<String> getNamesFor(Class<?> extensionPoint) { try { if (extensionPoint==GeoServerUserGroupService.class) return manager.listUserGroupServices(); if (extensionPoint==GeoServerRoleService.class) return manager.listRoleServices(); if (extensionPoint==GeoServerAuthenticationProvider.class) return manager.listAuthenticationProviders(); if (extensionPoint==AuthenticationProvider.class) return manager.listAuthenticationProviders(); if (extensionPoint==GeoServerSecurityFilter.class) return manager.listFilters(); if (extensionPoint==PasswordValidator.class) return manager.listPasswordValidators(); if (extensionPoint==MasterPasswordProvider.class) { return manager.listMasterPasswordProviders(); } } catch (IOException ex) { throw new RuntimeException(ex); } throw new RuntimeException("Unkwnown extension point: "+extensionPoint.getName()); } public void validateAddNamedService(Class<?> extensionPoint,SecurityNamedServiceConfig config) throws SecurityConfigException{ checkExtensionPont(extensionPoint, config.getClassName()); checkServiceName(extensionPoint, config.getName()); SortedSet<String> names= getNamesFor(extensionPoint); if (names.contains(config.getName())) throw createSecurityException(alreadyExistsErrorCode(extensionPoint), config.getName()); } public void validateModifiedNamedService(Class<?> extensionPoint,SecurityNamedServiceConfig config) throws SecurityConfigException{ checkExtensionPont(extensionPoint, config.getClassName()); checkServiceName(extensionPoint, config.getName()); SortedSet<String> names= getNamesFor(extensionPoint); if (names.contains(config.getName())==false) throw createSecurityException(notFoundErrorCode(extensionPoint),config.getName()); } public void validateRemoveNamedService(Class<?> extensionPoint,SecurityNamedServiceConfig config) throws SecurityConfigException{ checkServiceName(extensionPoint, config.getName()); } public void validateAddUserGroupService(SecurityUserGroupServiceConfig config) throws SecurityConfigException{ validateAddNamedService(GeoServerUserGroupService.class, config); validate(config); } public void validateAddRoleService(SecurityRoleServiceConfig config) throws SecurityConfigException{ validateAddNamedService(GeoServerRoleService.class, config); validate(config); } public void validateAddPasswordPolicy(PasswordPolicyConfig config) throws SecurityConfigException{ validateAddNamedService(PasswordValidator.class, config); validate(config); } public void validateAddAuthProvider(SecurityAuthProviderConfig config) throws SecurityConfigException{ validateAddNamedService(GeoServerAuthenticationProvider.class, config); validate(config); } public void validateAddFilter(SecurityNamedServiceConfig config) throws SecurityConfigException{ validateAddNamedService(GeoServerSecurityFilter.class, config); } public void validateAddMasterPasswordProvider(MasterPasswordProviderConfig config) throws SecurityConfigException { validateAddNamedService(MasterPasswordProvider.class, config); validate(config); } public void validateModifiedUserGroupService(SecurityUserGroupServiceConfig config,SecurityUserGroupServiceConfig oldConfig) throws SecurityConfigException{ validateModifiedNamedService(GeoServerUserGroupService.class, config); validate(config); } public void validateModifiedRoleService(SecurityRoleServiceConfig config,SecurityRoleServiceConfig oldConfig) throws SecurityConfigException{ validateModifiedNamedService(GeoServerRoleService.class, config); validate(config); } public void validateModifiedPasswordPolicy(PasswordPolicyConfig config,PasswordPolicyConfig oldConfig) throws SecurityConfigException{ validateModifiedNamedService(PasswordValidator.class, config); validate(config); } public void validateModifiedAuthProvider(SecurityAuthProviderConfig config,SecurityAuthProviderConfig oldconfig) throws SecurityConfigException{ validateModifiedNamedService(GeoServerAuthenticationProvider.class, config); validate(config); } public void validateModifiedFilter(SecurityNamedServiceConfig config,SecurityNamedServiceConfig oldConfig) throws SecurityConfigException{ validateModifiedNamedService(GeoServerSecurityFilter.class, config); } public void validateModifiedMasterPasswordProvider(MasterPasswordProviderConfig config, MasterPasswordProviderConfig oldConfig) throws SecurityConfigException { validateModifiedNamedService(MasterPasswordProvider.class, config); validate(config); } public void validateRemoveUserGroupService(SecurityUserGroupServiceConfig config) throws SecurityConfigException{ validateRemoveNamedService(GeoServerUserGroupService.class, config); try { for (String name: manager.listAuthenticationProviders()) { SecurityAuthProviderConfig authConfig = manager.loadAuthenticationProviderConfig(name); String userGroupService=authConfig.getUserGroupServiceName(); if (isNotEmpty(userGroupService)) { if (authConfig.getUserGroupServiceName().equals(config.getName())) throw createSecurityException(USERGROUP_SERVICE_ACTIVE_$2, config.getName(),authConfig.getName()); } } } catch (IOException ex) { throw new RuntimeException(ex); } } public void validateRemoveRoleService(SecurityRoleServiceConfig config) throws SecurityConfigException{ validateRemoveNamedService(GeoServerRoleService.class, config); if (manager.getActiveRoleService().getName().equals(config.getName())) { throw createSecurityException(ROLE_SERVICE_ACTIVE_$1, config.getName()); } } public void validateRemovePasswordPolicy(PasswordPolicyConfig config) throws SecurityConfigException{ validateRemoveNamedService(PasswordValidator.class, config); if (PasswordValidator.MASTERPASSWORD_NAME.equals(config.getName())) throw createSecurityException(PASSWD_POLICY_MASTER_DELETE); try { for (String name: manager.listUserGroupServices()) { SecurityUserGroupServiceConfig ugConfig = manager.loadUserGroupServiceConfig(name); if (ugConfig.getPasswordPolicyName().equals(config.getName())) throw createSecurityException(PASSWD_POLICY_ACTIVE_$2, config.getName(),ugConfig.getName()); } } catch (IOException ex) { throw new RuntimeException(ex); } } public void validateRemoveAuthProvider(SecurityAuthProviderConfig config) throws SecurityConfigException{ validateRemoveNamedService(GeoServerAuthenticationProvider.class, config); for (GeoServerAuthenticationProvider prov :manager.getAuthenticationProviders()) { if (prov.getName().equals(config.getName())) throw createSecurityException(AUTH_PROVIDER_ACTIVE_$1, config.getName()); } } public void validateRemoveFilter(SecurityNamedServiceConfig config) throws SecurityConfigException{ validateRemoveNamedService(GeoServerSecurityFilter.class, config); List<String> patterns = manager.getSecurityConfig().getFilterChain().patternsForFilter(config.getClassName(),false); if (patterns.isEmpty()==false) { throw createSecurityException(SecurityConfigException.FILTER_STILL_USED, config.getName(), StringUtils.arrayToCommaDelimitedString(patterns.toArray())); } } public void validateRemoveMasterPasswordProvider(MasterPasswordProviderConfig config) throws SecurityConfigException { validateRemoveNamedService(MasterPasswordProvider.class, config); } public void validate(SecurityAuthProviderConfig config) throws SecurityConfigException { if (isNotEmpty(config.getUserGroupServiceName())) { if (getNamesFor(GeoServerUserGroupService.class). contains(config.getUserGroupServiceName())==false) throw createSecurityException(USERGROUP_SERVICE_NOT_FOUND_$1, config.getUserGroupServiceName() ); } } public void validate(SecurityRoleServiceConfig config) throws SecurityConfigException { for (GeoServerRole systemRole : GeoServerRole.SystemRoles) { if (systemRole.getAuthority().equals(config.getAdminRoleName())) throw createSecurityException(RESERVED_ROLE_NAME,systemRole.getAuthority()); if (systemRole.getAuthority().equals(config.getGroupAdminRoleName())) throw createSecurityException(RESERVED_ROLE_NAME,systemRole.getAuthority()); } } public void validate(SecurityUserGroupServiceConfig config) throws SecurityConfigException { String encoderName =config.getPasswordEncoderName(); GeoServerPasswordEncoder encoder = null; if (isNotEmpty(encoderName)) { try { encoder = manager.loadPasswordEncoder(encoderName); } catch (NoSuchBeanDefinitionException ex) { throw createSecurityException(INVALID_CONFIG_PASSWORD_ENCODER_$1, encoderName); } if (encoder == null) { throw createSecurityException(INVALID_CONFIG_PASSWORD_ENCODER_$1, encoderName); } } else { throw createSecurityException(PASSWD_ENCODER_REQUIRED_$1, config.getName()); } if (!manager.isStrongEncryptionAvailable()) { if (encoder!=null && encoder.isAvailableWithoutStrongCryptogaphy()==false) { throw createSecurityException(INVALID_STRONG_PASSWORD_ENCODER); } } String policyName= config.getPasswordPolicyName(); if (isNotEmpty(policyName)==false) { throw createSecurityException(PASSWD_POLICY_REQUIRED_$1, config.getName()); } if (getNamesFor(PasswordValidator.class).contains(policyName)==false) { throw createSecurityException(PASSWD_POLICY_NOT_FOUND_$1,policyName); } } public void validate(PasswordPolicyConfig config) throws SecurityConfigException { if (config.getMinLength() < 0) throw createSecurityException(INVALID_MIN_LENGTH); if (config.getMaxLength() !=- 1) { if (config.getMinLength()>config.getMaxLength()) throw createSecurityException(INVALID_MAX_LENGTH); } } public void validate(MasterPasswordProviderConfig config) throws SecurityConfigException { } protected String alreadyExistsErrorCode(Class<?> extPoint) { if (GeoServerAuthenticationProvider.class==extPoint) return AUTH_PROVIDER_ALREADY_EXISTS_$1; if (PasswordValidator.class==extPoint) return PASSWD_POLICY_ALREADY_EXISTS_$1; if (GeoServerRoleService.class==extPoint) return ROLE_SERVICE_ALREADY_EXISTS_$1; if (GeoServerUserGroupService.class==extPoint) return USERGROUP_SERVICE_ALREADY_EXISTS_$1; if (GeoServerSecurityFilter.class==extPoint) return AUTH_FILTER_ALREADY_EXISTS_$1; throw new RuntimeException("Unknown extension point: "+extPoint.getName()); } protected String notFoundErrorCode(Class<?> extPoint) { if (GeoServerAuthenticationProvider.class==extPoint) return AUTH_PROVIDER_NOT_FOUND_$1; if (PasswordValidator.class==extPoint) return PASSWD_POLICY_NOT_FOUND_$1; if (GeoServerRoleService.class==extPoint) return ROLE_SERVICE_NOT_FOUND_$1; if (GeoServerUserGroupService.class==extPoint) return USERGROUP_SERVICE_NOT_FOUND_$1; if (GeoServerSecurityFilter.class==extPoint) return AUTH_FILTER_NOT_FOUND_$1; throw new RuntimeException("Unknown extension point: "+extPoint.getName()); } /** * Helper method for creating a proper * {@link SecurityConfigException} object */ protected SecurityConfigException createSecurityException (String errorid, Object ...args) { return new SecurityConfigException(errorid,args); } }