diff --git a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties index 2ba46c336b9..ec6d75de0d7 100644 --- a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties +++ b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties @@ -119,6 +119,3 @@ audit_jaas_client_option_useTicketCache=true audit_jaas_client_option_serviceName=ranger audit_jaas_client_option_keyTab=/etc/keytabs/rangeradmin.keytab audit_jaas_client_option_principal=rangeradmin/ranger.rangernw@EXAMPLE.COM - -#-- SSO Configs --# -sso_enabled=true diff --git a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties index f83688c8006..a29e128010e 100644 --- a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties +++ b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties @@ -120,6 +120,3 @@ audit_jaas_client_option_useTicketCache=true audit_jaas_client_option_serviceName=ranger audit_jaas_client_option_keyTab=/etc/keytabs/rangeradmin.keytab audit_jaas_client_option_principal=rangeradmin/ranger.rangernw@EXAMPLE.COM - -#-- SSO Configs --# -sso_enabled=true diff --git a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties index a66fb58b56a..0b1d3f5606c 100644 --- a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties +++ b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties @@ -126,6 +126,3 @@ audit_jaas_client_option_serviceName= audit_jaas_client_option_useTicketCache=false audit_jaas_client_option_keyTab=/etc/keytabs/rangeradmin.keytab audit_jaas_client_option_principal=rangeradmin/ranger.rangernw@EXAMPLE.COM - -#-- SSO Configs --# -sso_enabled=true diff --git a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties index 87c36a0357a..40f77c3fd4a 100644 --- a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties +++ b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties @@ -117,6 +117,3 @@ audit_jaas_client_1_option_useTicketCache=true audit_jaas_client_1_option_serviceName=ranger audit_jaas_client_1_option_keyTab=/etc/keytabs/rangeradmin.keytab audit_jaas_client_1_option_principal=rangeradmin/ranger.rangernw@EXAMPLE.COM - -#-- SSO Configs --# -sso_enabled=true diff --git a/security-admin/scripts/install.properties b/security-admin/scripts/install.properties index 6a6ababc0fb..ed0f2e0370f 100644 --- a/security-admin/scripts/install.properties +++ b/security-admin/scripts/install.properties @@ -256,14 +256,15 @@ audit_jaas_client_option_keyTab= audit_jaas_client_option_principal= hadoop_conf=/etc/hadoop/conf # -#-------- SSO CONFIG - Start ------------------ +#-------- JWT (Bearer token) AUTH CONFIG - Start ------------------ +# JWT authentication (RANGER-3739) activates automatically when a provider URL or public key is configured. +# Leave both empty to disable JWT authentication. # -sso_enabled=false -sso_providerurl=https://127.0.0.1:8443/gateway/knoxsso/api/v1/websso -sso_publickey= +jwt_providerurl= +jwt_publickey= # -#-------- SSO CONFIG - END ------------------ +#-------- JWT (Bearer token) AUTH CONFIG - END ------------------ # Custom log directory path RANGER_ADMIN_LOG_DIR=$PWD diff --git a/security-admin/scripts/setup.sh b/security-admin/scripts/setup.sh index b0bc262148a..a824ff53bfc 100755 --- a/security-admin/scripts/setup.sh +++ b/security-admin/scripts/setup.sh @@ -172,9 +172,8 @@ sqlserver_audit_file=$(get_prop 'sqlserver_audit_file' $PROPFILE) sqlanywhere_core_file=$(get_prop 'sqlanywhere_core_file' $PROPFILE) sqlanywhere_audit_file=$(get_prop 'sqlanywhere_audit_file' $PROPFILE) cred_keystore_filename=$(eval echo "$(get_prop 'cred_keystore_filename' $PROPFILE)") -sso_enabled=$(get_prop 'sso_enabled' $PROPFILE) -sso_providerurl=$(get_prop 'sso_providerurl' $PROPFILE) -sso_publickey=$(get_prop 'sso_publickey' $PROPFILE) +jwt_providerurl=$(get_prop 'jwt_providerurl' $PROPFILE) +jwt_publickey=$(get_prop 'jwt_publickey' $PROPFILE) FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=$(get_prop_or_default 'FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION' $PROPFILE 'false') RANGER_ADMIN_LOG_DIR=$(eval echo "$(get_prop 'RANGER_ADMIN_LOG_DIR' $PROPFILE)") RANGER_ADMIN_LOGBACK_CONF_FILE=$(eval echo "$(get_prop 'RANGER_ADMIN_LOGBACK_CONF_FILE' $PROPFILE)") @@ -1059,38 +1058,9 @@ update_properties() { updatePropertyToFilePyIfNotEmpty 'xasecure.audit.jaas.Client.option.keyTab' "$(get_prop_or_default 'audit_jaas_client_option_keyTab' $PROPFILE '')" $to_file_ranger updatePropertyToFilePyIfNotEmpty 'xasecure.audit.jaas.Client.option.principal' "$(get_prop_or_default 'audit_jaas_client_option_principal' $PROPFILE '')" $to_file_ranger - if [ "${sso_enabled}" == "" ] - then - sso_enabled="false" - fi - - sso_enabled=`echo $sso_enabled | tr '[:upper:]' '[:lower:]'` - - if [ "${sso_enabled}" == "true" ] - then - if [ "${sso_providerurl}" == "" ] || [ "${sso_publickey}" == "" ] - then - log "[E] Please provide valid values in SSO config properties!"; - exit 1 - fi - propertyName=ranger.sso.enabled - newPropertyValue="${sso_enabled}" - updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_ranger - - propertyName=ranger.sso.providerurl - newPropertyValue="${sso_providerurl}" - updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_ranger - - propertyName=ranger.sso.publicKey - newPropertyValue="${sso_publickey}" - updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_ranger - - else - propertyName=ranger.sso.enabled - newPropertyValue="false" - updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_ranger - - fi + # JWT (Bearer token) authentication (RANGER-3739) activates automatically when a provider URL or public key is set. + updatePropertyToFilePyIfNotEmpty 'ranger.admin.jwt.providerurl' "${jwt_providerurl}" $to_file_ranger + updatePropertyToFilePyIfNotEmpty 'ranger.admin.jwt.publickey' "${jwt_publickey}" $to_file_ranger ff_enable_ozone_action_matches_condition=$(echo "${FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION}" | tr '[:upper:]' '[:lower:]') if [ "${ff_enable_ozone_action_matches_condition}" != "true" ] diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RangerBizUtil.java b/security-admin/src/main/java/org/apache/ranger/biz/RangerBizUtil.java index 9219a86a10b..a0267e284f3 100644 --- a/security-admin/src/main/java/org/apache/ranger/biz/RangerBizUtil.java +++ b/security-admin/src/main/java/org/apache/ranger/biz/RangerBizUtil.java @@ -1088,16 +1088,6 @@ public boolean checkUserAccessible(VXUser vXUser) { return isAccessible; } - public boolean isSSOEnabled() { - UserSessionBase session = ContextUtil.getCurrentUserSession(); - - if (session != null) { - return session.isSSOEnabled() == null ? PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false) : session.isSSOEnabled(); - } else { - throw restErrorUtil.createRESTException("User session is not created", MessageEnums.OPER_NOT_ALLOWED_FOR_STATE); - } - } - public boolean isUserAllowed(RangerService rangerService, String cfgNameAllowedUsers) { Map map = rangerService.getConfigs(); String user = null; diff --git a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java index 519a89155f3..56e05f161c2 100644 --- a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java +++ b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java @@ -121,7 +121,7 @@ public UserSessionBase processSuccessLogin(int authType, String userAgent, HttpS } if (newSessionCreation) { - getSSOSpnegoAuthCheckForAPI(currentLoginId, httpRequest); + createExternalUserIfAbsent(currentLoginId, authType, httpRequest); // Need to build the UserSession XXPortalUser gjUser = daoManager.getXXPortalUser().findByLoginId(currentLoginId); @@ -188,20 +188,6 @@ public UserSessionBase processSuccessLogin(int authType, String userAgent, HttpS userSession.setSpnegoEnabled(true); } - boolean ssoEnabled; - - if (authType == XXAuthSession.AUTH_TYPE_TRUSTED_PROXY) { - ssoEnabled = true; - } else { - Object ssoEnabledObj = httpRequest.getAttribute("ssoEnabled"); - - ssoEnabled = ssoEnabledObj != null ? Boolean.parseBoolean(String.valueOf(ssoEnabledObj)) : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false); - } - - logger.debug("session id = {} ssoenabled = {}", userSession.getLoginId(), ssoEnabled); - - userSession.setSSOEnabled(ssoEnabled); - resetUserSessionForProfiles(userSession); resetUserModulePermission(userSession); @@ -522,23 +508,37 @@ protected XXAuthSession storeAuthSession(XXAuthSession gjAuthSession) { return dbMAuthSession; } - private void getSSOSpnegoAuthCheckForAPI(String currentLoginId, HttpServletRequest request) { - RangerSecurityContext context = RangerContextHolder.getSecurityContext(); - UserSessionBase session = context != null ? context.getUserSession() : null; - boolean ssoEnabled = session != null ? session.isSSOEnabled() : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false); - XXPortalUser gjUser = daoManager.getXXPortalUser().findByLoginId(currentLoginId); - - if (gjUser == null && ((request.getAttribute("spnegoEnabled") != null && (boolean) request.getAttribute("spnegoEnabled")) || (ssoEnabled))) { - logger.debug("User : {} doesn't exist in Ranger DB So creating user as it's SSO or Spnego authenticated", currentLoginId); + /** + * Auto-provisions a Ranger DB user for externally-authenticated principals that do not yet exist. + * Provisioning is driven by the actual authentication mechanism of the request (SPNEGO/Kerberos, + * trusted-proxy header auth, JWT bearer auth, or the internal health-check user) rather than the + * removed {@code ranger.sso.enabled} flag. Password/LDAP/AD logins are never auto-provisioned here + * since those principals must already exist to authenticate. + */ + private void createExternalUserIfAbsent(String currentLoginId, int authType, HttpServletRequest request) { + XXPortalUser gjUser = daoManager.getXXPortalUser().findByLoginId(currentLoginId); + if (gjUser == null) { if (bizUtil.isHealthCheckUser(currentLoginId)) { + logger.debug("User : {} doesn't exist in Ranger DB. Creating healthcheck user synchronously.", currentLoginId); + xUserMgr.createServiceConfigUserSynchronously(currentLoginId); - } else { + } else if (isExternallyAuthenticated(authType, request)) { + logger.debug("User : {} doesn't exist in Ranger DB. Creating user as it is externally authenticated (SPNEGO/trusted-proxy/JWT).", currentLoginId); + xUserMgr.createServiceConfigUser(currentLoginId); } } } + private boolean isExternallyAuthenticated(int authType, HttpServletRequest request) { + boolean spnegoEnabled = request.getAttribute("spnegoEnabled") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("spnegoEnabled"))); + boolean jwtAuthenticated = request.getAttribute("jwtAuthenticated") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("jwtAuthenticated"))); + boolean trustedProxy = authType == XXAuthSession.AUTH_TYPE_TRUSTED_PROXY; + + return spnegoEnabled || jwtAuthenticated || trustedProxy; + } + private void setUserRoles(UserSessionBase userSession) { List strRoleList = new ArrayList<>(); List roleList = daoManager.getXXPortalUserRole().findByUserId(userSession.getUserId()); diff --git a/security-admin/src/main/java/org/apache/ranger/common/UserSessionBase.java b/security-admin/src/main/java/org/apache/ranger/common/UserSessionBase.java index 08bb55dde80..b87af38e65b 100644 --- a/security-admin/src/main/java/org/apache/ranger/common/UserSessionBase.java +++ b/security-admin/src/main/java/org/apache/ranger/common/UserSessionBase.java @@ -42,7 +42,6 @@ public class UserSessionBase implements Serializable { private int authProvider = RangerConstants.USER_APP; private List userRoleList = new ArrayList<>(); private RangerUserPermission rangerUserPermission; - private Boolean isSSOEnabled; private Boolean isSpnegoEnabled = Boolean.FALSE; public Long getUserId() { @@ -170,14 +169,6 @@ public void setRangerUserPermission(RangerUserPermission rangerUserPermission) { this.rangerUserPermission = rangerUserPermission; } - public Boolean isSSOEnabled() { - return isSSOEnabled; - } - - public void setSSOEnabled(Boolean isSSOEnabled) { - this.isSSOEnabled = isSSOEnabled; - } - public Boolean isSpnegoEnabled() { return isSpnegoEnabled; } diff --git a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java index 25534c214be..ad4d56d195c 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java @@ -2948,13 +2948,6 @@ public List getPoliciesWithMetaAttributes(List polic return svcStore.getPoliciesWithMetaAttributes(policies); } - @GET - @Path("/checksso") - @Produces(MediaType.TEXT_PLAIN) - public String checkSSO() { - return String.valueOf(bizUtil.isSSOEnabled()); - } - @GET @Path("/csrfconf") @Produces("application/json") diff --git a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java index a21d08ea0cd..99904ef0c7f 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java +++ b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java @@ -85,7 +85,6 @@ public class RangerAuthenticationProvider implements AuthenticationProvider { private String rangerAuthenticationMethod; private LdapAuthenticator authenticator; - private boolean ssoEnabled; public RangerAuthenticationProvider() { this.isFipsEnabled = RangerAdminConfig.getInstance().isFipsEnabled(); @@ -93,121 +92,111 @@ public RangerAuthenticationProvider() { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (isSsoEnabled()) { - if (authentication != null) { - authentication = getSSOAuthentication(authentication); + String sha256PasswordUpdateDisable = PropertiesUtil.getProperty("ranger.sha256Password.update.disable", "false"); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - } - } else { - String sha256PasswordUpdateDisable = PropertiesUtil.getProperty("ranger.sha256Password.update.disable", "false"); - - if (rangerAuthenticationMethod == null) { - rangerAuthenticationMethod = "NONE"; - } - - if (authentication != null) { - if ("LDAP".equalsIgnoreCase(rangerAuthenticationMethod)) { - authentication = getLdapAuthentication(authentication); + if (rangerAuthenticationMethod == null) { + rangerAuthenticationMethod = "NONE"; + } - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } else { - authentication = getLdapBindAuthentication(authentication); + if (authentication != null) { + if ("LDAP".equalsIgnoreCase(rangerAuthenticationMethod)) { + authentication = getLdapAuthentication(authentication); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - } - } else if ("ACTIVE_DIRECTORY".equalsIgnoreCase(rangerAuthenticationMethod)) { - authentication = getADBindAuthentication(authentication); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } else { + authentication = getLdapBindAuthentication(authentication); if (authentication != null && authentication.isAuthenticated()) { return authentication; - } else { - authentication = getADAuthentication(authentication); - - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } } - } else if ("UNIX".equalsIgnoreCase(rangerAuthenticationMethod)) { - boolean isPAMAuthEnabled = PropertiesUtil.getBooleanProperty("ranger.pam.authentication.enabled", false); - - authentication = (isPAMAuthEnabled ? getPamAuthentication(authentication) : getUnixAuthentication(authentication)); + } + } else if ("ACTIVE_DIRECTORY".equalsIgnoreCase(rangerAuthenticationMethod)) { + authentication = getADBindAuthentication(authentication); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - } else if ("PAM".equalsIgnoreCase(rangerAuthenticationMethod)) { - authentication = getPamAuthentication(authentication); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } else { + authentication = getADAuthentication(authentication); if (authentication != null && authentication.isAuthenticated()) { return authentication; } } + } else if ("UNIX".equalsIgnoreCase(rangerAuthenticationMethod)) { + boolean isPAMAuthEnabled = PropertiesUtil.getBooleanProperty("ranger.pam.authentication.enabled", false); - // Following are JDBC - if (authentication != null && authentication.getName() != null && sessionMgr.isLoginIdLocked(authentication.getName())) { - logger.debug("Failed to authenticate since user account is locked"); + authentication = (isPAMAuthEnabled ? getPamAuthentication(authentication) : getUnixAuthentication(authentication)); - throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; } + } else if ("PAM".equalsIgnoreCase(rangerAuthenticationMethod)) { + authentication = getPamAuthentication(authentication); - if (this.isFipsEnabled) { - try { - authentication = getJDBCAuthentication(authentication, ""); - } catch (Exception e) { - logger.error("JDBC Authentication failure: ", e); - throw e; - } - + if (authentication != null && authentication.isAuthenticated()) { return authentication; } + } + + // Following are JDBC + if (authentication != null && authentication.getName() != null && sessionMgr.isLoginIdLocked(authentication.getName())) { + logger.debug("Failed to authenticate since user account is locked"); - String encoder = "SHA256"; + throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); + } + if (this.isFipsEnabled) { try { - authentication = getJDBCAuthentication(authentication, encoder); + authentication = getJDBCAuthentication(authentication, ""); } catch (Exception e) { - logger.debug("JDBC Authentication failure: ", e); + logger.error("JDBC Authentication failure: ", e); + throw e; } - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } + return authentication; + } - if (authentication != null && !authentication.isAuthenticated()) { - logger.info("Authentication with SHA-256 failed. Now trying with MD5."); + String encoder = "SHA256"; - encoder = "MD5"; + try { + authentication = getJDBCAuthentication(authentication, encoder); + } catch (Exception e) { + logger.debug("JDBC Authentication failure: ", e); + } - String userName = authentication.getName(); - String userPassword = null; + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } + if (authentication != null && !authentication.isAuthenticated()) { + logger.info("Authentication with SHA-256 failed. Now trying with MD5."); - try { - authentication = getJDBCAuthentication(authentication, encoder); - } catch (Exception e) { - throw e; - } + encoder = "MD5"; - if (authentication != null && authentication.isAuthenticated()) { - if ("false".equalsIgnoreCase(sha256PasswordUpdateDisable)) { - userMgr.updatePasswordInSHA256(userName, userPassword, false); - } - } + String userName = authentication.getName(); + String userPassword = null; - return authentication; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + try { + authentication = getJDBCAuthentication(authentication, encoder); + } catch (Exception e) { + throw e; + } + + if (authentication != null && authentication.isAuthenticated()) { + if ("false".equalsIgnoreCase(sha256PasswordUpdateDisable)) { + userMgr.updatePasswordInSHA256(userName, userPassword, false); + } } return authentication; } + + return authentication; } return authentication; @@ -398,20 +387,6 @@ public Authentication getAuthenticationWithGrantedAuthority(Authentication authe return authentication; } - /** - * @return the ssoEnabled - */ - public boolean isSsoEnabled() { - return ssoEnabled; - } - - /** - * @param ssoEnabled the ssoEnabled to set - */ - public void setSsoEnabled(boolean ssoEnabled) { - this.ssoEnabled = ssoEnabled; - } - private Authentication getLdapAuthentication(Authentication authentication) { try { // getting ldap settings @@ -711,8 +686,4 @@ private List getAuthorities(String username) { return grantedAuths; } - - private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException { - return authentication; - } } diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/authentication/RangerAuthenticationEntryPoint.java b/security-admin/src/main/java/org/apache/ranger/security/web/authentication/RangerAuthenticationEntryPoint.java index cf29bb97bcb..0765e73b45f 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/web/authentication/RangerAuthenticationEntryPoint.java +++ b/security-admin/src/main/java/org/apache/ranger/security/web/authentication/RangerAuthenticationEntryPoint.java @@ -26,7 +26,6 @@ import org.apache.ranger.common.JSONUtil; import org.apache.ranger.common.PropertiesUtil; import org.apache.ranger.common.RangerConfigUtil; -import org.apache.ranger.util.RestUtil; import org.apache.ranger.view.VXResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,13 +113,6 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A response.sendError(ajaxReturnCode, ""); } else if (!(requestURI.contains(servletPath))) { - if (requestURI.contains(RestUtil.LOCAL_LOGIN_URL)) { - if (request.getSession() != null) { - request.getSession().setAttribute("locallogin", "true"); - request.getServletContext().setAttribute(request.getSession().getId(), "locallogin"); - } - } - super.commence(request, response, authException); } } diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthConfig.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthConfig.java new file mode 100644 index 00000000000..fd73dbfe159 --- /dev/null +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthConfig.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.ranger.security.web.filter; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.common.PropertiesUtil; + +/** + * Ranger Admin JWT (Bearer token) authentication configuration. + * + * JWT authentication is provided by {@link RangerJwtAuthFilter} / {@link RangerJwtAuthWrapper} + * (RANGER-3739). Configuration uses the {@code ranger.admin.jwt.*} namespace. For backward + * compatibility with deployments configured before the Knox SSO filter was removed, the + * legacy {@code ranger.sso.*} property names are still honored as a fallback. + */ +public final class RangerJwtAuthConfig { + public static final String PROVIDER_URL = "ranger.admin.jwt.providerurl"; + public static final String PUBLIC_KEY = "ranger.admin.jwt.publickey"; + public static final String AUDIENCES = "ranger.admin.jwt.audiences"; + public static final String ISSUER = "ranger.admin.jwt.issuer"; + public static final String BROWSER_USERAGENT = "ranger.admin.jwt.browser.useragent"; + public static final String DEFAULT_BROWSER_USERAGENT = "ranger.default.browser-useragents"; + public static final String HEALTH_CHECK_URI = "/actuator/health"; + + // Deprecated legacy property names (pre RANGER-685 Knox SSO removal). Retained as fallbacks only. + private static final String LEGACY_PROVIDER_URL = "ranger.sso.providerurl"; + private static final String LEGACY_PUBLIC_KEY = "ranger.sso.publicKey"; + private static final String LEGACY_AUDIENCES = "ranger.sso.audiences"; + private static final String LEGACY_ISSUER = "ranger.sso.issuer"; + private static final String LEGACY_BROWSER_USERAGENT = "ranger.sso.browser.useragent"; + + private RangerJwtAuthConfig() { + } + + public static String getProviderUrl() { + return resolve(PROVIDER_URL, LEGACY_PROVIDER_URL); + } + + public static String getPublicKey() { + return resolve(PUBLIC_KEY, LEGACY_PUBLIC_KEY); + } + + public static String getAudiences() { + return resolve(AUDIENCES, LEGACY_AUDIENCES); + } + + public static String getIssuer() { + return resolve(ISSUER, LEGACY_ISSUER); + } + + public static String getBrowserUserAgent() { + return resolve(BROWSER_USERAGENT, LEGACY_BROWSER_USERAGENT); + } + + private static String resolve(String preferredKey, String legacyKey) { + String value = PropertiesUtil.getProperty(preferredKey); + + if (StringUtils.isBlank(value)) { + value = PropertiesUtil.getProperty(legacyKey); + } + + return value; + } +} diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthFilter.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthFilter.java index 72cc9f8ff81..14047a29309 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthFilter.java +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthFilter.java @@ -18,6 +18,7 @@ */ package org.apache.ranger.security.web.filter; +import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AuthorizationException; @@ -71,17 +72,24 @@ public void initialize() { * DelegatingFilterProxy} does not invoke init method (like Servlet container). */ try { - Properties config = new Properties(); + String providerUrl = StringUtils.trimToNull(RangerJwtAuthConfig.getProviderUrl()); + String publicKey = StringUtils.trimToNull(RangerJwtAuthConfig.getPublicKey()); - config.setProperty(RangerJwtAuthHandler.KEY_PROVIDER_URL, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_AUTH_PROVIDER_URL)); - config.setProperty(RangerJwtAuthHandler.KEY_JWT_PUBLIC_KEY, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_PUBLIC_KEY, "")); - config.setProperty(RangerJwtAuthHandler.KEY_JWT_AUDIENCES, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_AUDIENCES, "")); - config.setProperty(RangerJwtAuthHandler.KEY_JWT_ISS, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_ISSUER, "")); + if (providerUrl == null && publicKey == null) { + LOG.info("JWT authentication is not configured (no provider URL or public key). Bearer token auth will be unavailable."); + } else { + Properties config = new Properties(); - super.initialize(config); + config.setProperty(RangerJwtAuthHandler.KEY_PROVIDER_URL, StringUtils.defaultString(providerUrl)); + config.setProperty(RangerJwtAuthHandler.KEY_JWT_PUBLIC_KEY, StringUtils.defaultString(publicKey)); + config.setProperty(RangerJwtAuthHandler.KEY_JWT_AUDIENCES, StringUtils.defaultString(RangerJwtAuthConfig.getAudiences())); + config.setProperty(RangerJwtAuthHandler.KEY_JWT_ISS, StringUtils.defaultString(RangerJwtAuthConfig.getIssuer())); - Configuration conf = getProxyuserConfiguration(); - ProxyUsers.refreshSuperUserGroupsConfiguration(conf, "ranger.proxyuser."); + super.initialize(config); + + Configuration conf = getProxyuserConfiguration(); + ProxyUsers.refreshSuperUserGroupsConfiguration(conf, "ranger.proxyuser."); + } } catch (Exception e) { LOG.error("Failed to initialize Ranger Admin JWT Auth Filter.", e); } @@ -110,6 +118,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha finalAuthentication.setDetails(webDetails); SecurityContextHolder.getContext().setAuthentication(finalAuthentication); + + // Marker used by downstream filters (audit auth-type classification and user auto-provisioning) + // to recognize a JWT-authenticated request without relying on the removed ranger.sso.enabled flag. + httpServletRequest.setAttribute("jwtAuthenticated", Boolean.TRUE); } // Log final status of request. diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthWrapper.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthWrapper.java index c87f2c70363..56fa741ed58 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthWrapper.java +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerJwtAuthWrapper.java @@ -20,9 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.ranger.common.PropertiesUtil; -import org.apache.ranger.common.UserSessionBase; -import org.apache.ranger.security.context.RangerContextHolder; -import org.apache.ranger.security.context.RangerSecurityContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -56,8 +53,8 @@ public class RangerJwtAuthWrapper extends GenericFilterBean { @PostConstruct public void initialize() { //FIXME: Browser agents should be common across ALL filters. - String defaultUserAgent = PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.DEFAULT_BROWSER_USERAGENT); - String userAgent = PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.BROWSER_USERAGENT); + String defaultUserAgent = PropertiesUtil.getProperty(RangerJwtAuthConfig.DEFAULT_BROWSER_USERAGENT); + String userAgent = RangerJwtAuthConfig.getBrowserUserAgent(); if (StringUtils.isBlank(userAgent) && StringUtils.isNotBlank(defaultUserAgent)) { userAgent = defaultUserAgent; @@ -72,16 +69,16 @@ public void initialize() { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { LOG.debug("===>>> RangerJwtAuthWrapper.doFilter({}, {}, {})", request, response, filterChain); - RangerSecurityContext context = RangerContextHolder.getSecurityContext(); - UserSessionBase session = context != null ? context.getUserSession() : null; - boolean ssoEnabled = session != null ? session.isSSOEnabled() : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false); - boolean useJwtAuthMechanism = request != null && !isRequestAuthenticated() && RangerJwtAuthFilter.canAuthenticateRequest(request); + HttpServletRequest httpRequest = (HttpServletRequest) request; + boolean useJwtAuthMechanism = request != null && !isRequestAuthenticated() && RangerJwtAuthFilter.canAuthenticateRequest(request); - if (!ssoEnabled && useJwtAuthMechanism) { + // Skip JWT processing for health probes so that /actuator/health(/liveness|/readiness) never gets + // redirected to the login page and works regardless of JWT configuration. + if (useJwtAuthMechanism && !isHealthCheckRequest(httpRequest)) { rangerJwtAuthFilter.doFilter(request, response, filterChain); if (!isRequestAuthenticated()) { - String userAgent = ((HttpServletRequest) request).getHeader("User-Agent"); + String userAgent = httpRequest.getHeader("User-Agent"); if (isBrowserAgent(userAgent)) { LOG.debug("Redirecting to login page as request does not have valid JWT auth details."); @@ -113,6 +110,12 @@ protected boolean isBrowserAgent(String userAgent) { return isBrowserAgent; } + private boolean isHealthCheckRequest(HttpServletRequest httpRequest) { + String requestUri = httpRequest.getRequestURI(); + + return StringUtils.isNotBlank(requestUri) && requestUri.contains(RangerJwtAuthConfig.HEALTH_CHECK_URI); + } + private boolean isRequestAuthenticated() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSSOAuthenticationFilter.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSSOAuthenticationFilter.java deleted file mode 100644 index d5ff86db040..00000000000 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSSOAuthenticationFilter.java +++ /dev/null @@ -1,666 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import com.google.inject.Inject; -import com.nimbusds.jose.JWSObject; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; -import org.apache.commons.lang3.StringUtils; -import org.apache.ranger.biz.UserMgr; -import org.apache.ranger.common.PropertiesUtil; -import org.apache.ranger.common.RangerConstants; -import org.apache.ranger.common.UserSessionBase; -import org.apache.ranger.security.context.RangerContextHolder; -import org.apache.ranger.security.context.RangerSecurityContext; -import org.apache.ranger.security.handler.RangerAuthenticationProvider; -import org.apache.ranger.util.RestUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.authentication.WebAuthenticationDetails; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.security.PublicKey; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPublicKey; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.List; - -public class RangerSSOAuthenticationFilter implements Filter { - private static final Logger LOG = LoggerFactory.getLogger(RangerSSOAuthenticationFilter.class); - - public static final String BROWSER_USERAGENT = "ranger.sso.browser.useragent"; - public static final String JWT_AUTH_PROVIDER_URL = "ranger.sso.providerurl"; - public static final String JWT_PUBLIC_KEY = "ranger.sso.publicKey"; - public static final String JWT_COOKIE_NAME = "ranger.sso.cookiename"; - public static final String JWT_AUDIENCES = "ranger.sso.audiences"; - public static final String JWT_ISSUER = "ranger.sso.issuer"; - public static final String JWT_ORIGINAL_URL_QUERY_PARAM = "ranger.sso.query.param.originalurl"; - public static final String JWT_COOKIE_NAME_DEFAULT = "hadoop-jwt"; - public static final String JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT = "originalUrl"; - /** - * If specified, this configuration property refers to the signature algorithm which a received - * token must match. Otherwise, the default value "RS256" is used - */ - public static final String JWT_EXPECTED_SIGALG = "ranger.sso.expected.sigalg"; - public static final String JWT_DEFAULT_SIGALG = "RS256"; - public static final String DEFAULT_BROWSER_USERAGENT = "ranger.default.browser-useragents"; - - private final SSOAuthenticationProperties jwtProperties; - - @Autowired - UserMgr userMgr; - - private String originalUrlQueryParam = "originalUrl"; - private String authenticationProviderUrl; - private RSAPublicKey publicKey; - private String cookieName = "hadoop-jwt"; - - @Inject - public RangerSSOAuthenticationFilter() { - jwtProperties = getJwtProperties(); - - loadJwtProperties(); - } - - public RangerSSOAuthenticationFilter(SSOAuthenticationProperties jwtProperties) { - this.jwtProperties = jwtProperties; - - loadJwtProperties(); - } - - public static RSAPublicKey parseRSAPublicKey(String pem) throws CertificateException, UnsupportedEncodingException, ServletException { - String pemHeader = "-----BEGIN CERTIFICATE-----\n"; - String pemFooter = "\n-----END CERTIFICATE-----"; - String fullPem = pemHeader + pem + pemFooter; - PublicKey key; - - try { - CertificateFactory fact = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = new ByteArrayInputStream(fullPem.getBytes(StandardCharsets.UTF_8)); - X509Certificate cer = (X509Certificate) fact.generateCertificate(is); - - key = cer.getPublicKey(); - } catch (CertificateException ce) { - String message; - - if (pem.startsWith(pemHeader)) { - message = "CertificateException - be sure not to include PEM header and footer in the PEM configuration element."; - } else { - message = "CertificateException - PEM may be corrupt"; - } - - throw new ServletException(message, ce); - } - return (RSAPublicKey) key; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - /* - * doFilter of RangerSSOAuthenticationFilter is the first in the filter list so in this it check for the request - * if the request is from browser, doesn't contain local login and sso is enabled then it process the request against knox sso - * else if it's ssoenable and the request is with local login string then it show's the appropriate msg - * else if ssoenable is false then it contiunes with further filters as it was before sso - */ - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; - String xForwardedURL = RestUtil.constructForwardableURL(httpRequest); - - if (httpRequest.getRequestedSessionId() != null && !httpRequest.isRequestedSessionIdValid()) { - synchronized (httpRequest.getServletContext()) { - if (httpRequest.getServletContext().getAttribute(httpRequest.getRequestedSessionId()) != null && "locallogin".equals(httpRequest.getServletContext().getAttribute(httpRequest.getRequestedSessionId()).toString())) { - httpRequest.getSession().setAttribute("locallogin", "true"); - httpRequest.getServletContext().removeAttribute(httpRequest.getRequestedSessionId()); - } - } - } - - RangerSecurityContext context = RangerContextHolder.getSecurityContext(); - UserSessionBase session = context != null ? context.getUserSession() : null; - boolean ssoEnabled = session != null ? session.isSSOEnabled() : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false); - String userAgent = httpRequest.getHeader("User-Agent"); - - if (httpRequest.getSession() != null) { - if (httpRequest.getSession().getAttribute("locallogin") != null) { - servletRequest.setAttribute("ssoEnabled", false); - - filterChain.doFilter(servletRequest, servletResponse); - - return; - } - } - - //If sso is enable and request is not for local login and is from browser then it will go inside and try for knox sso authentication - if (ssoEnabled && !httpRequest.getRequestURI().contains(RestUtil.LOCAL_LOGIN_URL)) { - //if jwt properties are loaded and is current not authenticated then it will go for sso authentication - //Note : Need to remove !isAuthenticated() after knoxsso solve the bug from cross-origin script - if (jwtProperties != null && !isAuthenticated()) { - HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; - String serializedJWT = getJWTFromCookie(httpRequest); - - // if we get the hadoop-jwt token from the cookies then will process it further - if (serializedJWT != null) { - SignedJWT jwtToken; - - try { - jwtToken = SignedJWT.parse(serializedJWT); - - boolean valid = validateToken(jwtToken); - - //if the public key provide is correct and also token is not expired the process token - if (valid) { - String userName = jwtToken.getJWTClaimsSet().getSubject(); - - LOG.info("SSO login user : {}", userName); - - String rangerLdapDefaultRole = PropertiesUtil.getProperty("ranger.ldap.default.role", "ROLE_USER"); - - //if we get the userName from the token then log into ranger using the same user - if (userName != null && !userName.trim().isEmpty()) { - final List grantedAuths = new ArrayList<>(); - - grantedAuths.add(new SimpleGrantedAuthority(rangerLdapDefaultRole)); - - final UserDetails principal = new User(userName, "", grantedAuths); - final AbstractAuthenticationToken finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); - WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); - - finalAuthentication.setDetails(webDetails); - - RangerAuthenticationProvider authenticationProvider = new RangerAuthenticationProvider(); - - authenticationProvider.setSsoEnabled(ssoEnabled); - - Authentication authentication = authenticationProvider.authenticate(finalAuthentication); - - authentication = getGrantedAuthority(authentication); - - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - filterChain.doFilter(servletRequest, httpServletResponse); - } - // if the token is not valid then redirect to knox sso - else { - if (isWebUserAgent(userAgent)) { - String ssourl; - String ajaxRequestHeader = httpRequest.getHeader("X-Requested-With"); - - if ("XMLHttpRequest".equals(ajaxRequestHeader)) { - ssourl = constructLoginURLForApi(httpRequest, xForwardedURL); - - LOG.debug("ajaxRequestHeader redirectUrl = {}", ssourl); - - httpServletResponse.setHeader("X-Frame-Options", "DENY"); - httpServletResponse.setStatus(RangerConstants.SC_AUTHENTICATION_TIMEOUT); - httpServletResponse.setHeader("X-Rngr-Redirect-Url", ssourl); - } else { - ssourl = RestUtil.constructRedirectURL(httpRequest, authenticationProviderUrl, xForwardedURL, originalUrlQueryParam); - - LOG.debug("SSO URL = {}", ssourl); - - httpServletResponse.sendRedirect(ssourl); - } - } else { - filterChain.doFilter(servletRequest, httpServletResponse); - } - } - } catch (ParseException e) { - LOG.warn("Unable to parse the JWT token", e); - } - } - // if the jwt token is not available then redirect it to knox sso - else { - if (isWebUserAgent(userAgent)) { - String ssourl; - String ajaxRequestHeader = httpRequest.getHeader("X-Requested-With"); - - if ("XMLHttpRequest".equals(ajaxRequestHeader)) { - ssourl = constructLoginURLForApi(httpRequest, xForwardedURL); - - LOG.debug("ajaxRequestHeader redirectUrl = {}", ssourl); - - httpServletResponse.setHeader("X-Frame-Options", "DENY"); - httpServletResponse.setStatus(RangerConstants.SC_AUTHENTICATION_TIMEOUT); - httpServletResponse.setHeader("X-Rngr-Redirect-Url", ssourl); - } else { - ssourl = RestUtil.constructRedirectURL(httpRequest, authenticationProviderUrl, xForwardedURL, originalUrlQueryParam); - - LOG.debug("SSO URL = {}", ssourl); - - httpServletResponse.sendRedirect(ssourl); - } - } else { - filterChain.doFilter(servletRequest, httpServletResponse); - } - } - } - //if property is not loaded or is already authenticated then proceed further with next filter - else { - filterChain.doFilter(servletRequest, servletResponse); - } - } else if (ssoEnabled && ((HttpServletRequest) servletRequest).getRequestURI().contains(RestUtil.LOCAL_LOGIN_URL) && isWebUserAgent(userAgent) && isAuthenticated()) { - //If already there's an active session with sso and user want's to switch to local login(i.e without sso) then it won't be navigated to local login - // In this scenario the user has to use separate browser - String url = ((HttpServletRequest) servletRequest).getRequestURI().replace(RestUtil.LOCAL_LOGIN_URL + "/", ""); - - url = url.replace(RestUtil.LOCAL_LOGIN_URL, ""); - - LOG.warn("There is an active session and if you want local login to ranger, try this on a separate browser"); - - ((HttpServletResponse) servletResponse).sendRedirect(url); - } else if (!ssoEnabled && ((HttpServletRequest) servletRequest).getRequestURI().contains(RestUtil.LOCAL_LOGIN_URL) && !isAuthenticated() && (isWebUserAgent(userAgent) || isBrowserAgent(userAgent))) { - // if sso is not enabled and request has locallogin then need to redirect user to the login page. - String url = ((HttpServletRequest) servletRequest).getRequestURI().replace(RestUtil.LOCAL_LOGIN_URL + "/", ""); - - url = url.replace(RestUtil.LOCAL_LOGIN_URL, "login.jsp"); - - // invalidating session - LOG.debug("Request does not have any authentication and contains local login url redirecting to login page."); - - ((HttpServletRequest) servletRequest).getSession().invalidate(); - - ((HttpServletResponse) servletResponse).sendRedirect(url); - } else { //if sso is not enable or the request is not from browser then proceed further with next filter - filterChain.doFilter(servletRequest, servletResponse); - } - } - - @Override - public void destroy() { - } - - public SSOAuthenticationProperties getJwtProperties() { - String providerUrl = PropertiesUtil.getProperty(JWT_AUTH_PROVIDER_URL); - - if (providerUrl != null && PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false)) { - SSOAuthenticationProperties jwtProperties = new SSOAuthenticationProperties(); - String publicKeyPath = PropertiesUtil.getProperty(JWT_PUBLIC_KEY); - - if (publicKeyPath == null) { - LOG.error("Public key pem not specified for SSO auth provider {}. SSO auth will be disabled.", providerUrl); - - return null; - } - - jwtProperties.setAuthenticationProviderUrl(providerUrl); - jwtProperties.setCookieName(PropertiesUtil.getProperty(JWT_COOKIE_NAME, JWT_COOKIE_NAME_DEFAULT)); - jwtProperties.setOriginalUrlQueryParam(PropertiesUtil.getProperty(JWT_ORIGINAL_URL_QUERY_PARAM, JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT)); - - String defaultUserAgent = PropertiesUtil.getProperty(DEFAULT_BROWSER_USERAGENT); - String userAgent = PropertiesUtil.getProperty(BROWSER_USERAGENT); - - if (userAgent != null && !userAgent.isEmpty()) { - jwtProperties.setUserAgentList(userAgent.split(",")); - } else if (defaultUserAgent != null && !defaultUserAgent.isEmpty()) { - userAgent = defaultUserAgent; - - jwtProperties.setUserAgentList(userAgent.split(",")); - } - - String audiences = PropertiesUtil.getProperty(JWT_AUDIENCES); - - if (audiences != null && !audiences.isEmpty()) { - jwtProperties.setAudiences(Arrays.asList(audiences.split(","))); - } - - jwtProperties.setExpectedSigAlg(PropertiesUtil.getProperty(JWT_EXPECTED_SIGALG, JWT_DEFAULT_SIGALG)); - - try { - RSAPublicKey publicKey = parseRSAPublicKey(publicKeyPath); - - jwtProperties.setPublicKey(publicKey); - } catch (IOException e) { - LOG.error("Unable to read public certificate file. JWT auth will be disabled.", e); - } catch (CertificateException e) { - LOG.error("Unable to parse public certificate file. JWT auth will be disabled.", e); - } catch (ServletException e) { - LOG.error("ServletException while processing the properties", e); - } - - return jwtProperties; - } else { - return null; - } - } - - /** - * Encapsulate the acquisition of the JWT token from HTTP cookies within the - * request. - * - * @param req servlet request to get the JWT token from - * @return serialized JWT token - */ - protected String getJWTFromCookie(HttpServletRequest req) { - String serializedJWT = null; - Cookie[] cookies = req.getCookies(); - - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookieName != null && cookieName.equals(cookie.getName())) { - LOG.debug("{} cookie has been found and is being processed", cookieName); - - serializedJWT = cookie.getValue(); - break; - } - } - } - - return serializedJWT; - } - - /** - * This method provides a single method for validating the JWT for use in - * request processing. It provides for the override of specific aspects of - * this implementation through submethods used within but also allows for - * the override of the entire token validation algorithm. - * - * @param jwtToken the token to validate - * @return true if valid - */ - protected boolean validateToken(SignedJWT jwtToken) { - boolean sigValid = validateSignature(jwtToken); - - if (!sigValid) { - LOG.warn("Signature of JWT token could not be verified. Please check the public key"); - - return false; - } - - boolean expValid = validateExpiration(jwtToken); - - if (!expValid) { - LOG.warn("Expiration time validation of JWT token failed."); - - return false; - } - - boolean audiencesValid = validateAudiences(jwtToken); - - if (!audiencesValid) { - LOG.warn("Audience validation of JWT token failed."); - - return false; - } - - return true; - } - - /** - * Verify the signature of the JWT token in this method. This method depends - * on the public key that was established during init based upon the - * provisioned public key. Override this method in subclasses in order to - * customize the signature verification behavior. - * - * @param jwtToken the token that contains the signature to be validated - * @return valid true if signature verifies successfully; false otherwise - */ - protected boolean validateSignature(SignedJWT jwtToken) { - boolean valid = false; - - if (JWSObject.State.SIGNED == jwtToken.getState()) { - LOG.debug("SSO token is in a SIGNED state"); - - if (jwtToken.getSignature() != null) { - LOG.debug("SSO token signature is not null"); - - try { - JWSVerifier verifier = new RSASSAVerifier(publicKey); - - if (jwtToken.verify(verifier)) { - valid = true; - - LOG.debug("SSO token has been successfully verified"); - } else { - LOG.warn("SSO signature verification failed.Please check the public key"); - } - } catch (Exception e) { - LOG.warn("Error while validating signature", e); - } - } - - // Now check that the signature algorithm was as expected - if (valid) { - String receivedSigAlg = jwtToken.getHeader().getAlgorithm().getName(); - - if (!receivedSigAlg.equals(jwtProperties.getExpectedSigAlg())) { - valid = false; - } - } - } - - return valid; - } - - /** - * Validate that the expiration time of the JWT token has not been violated. - * If it has then throw an AuthenticationException. Override this method in - * subclasses in order to customize the expiration validation behavior. - * - * @param jwtToken the token that contains the expiration date to validate - * @return valid true if the token has not expired; false otherwise - */ - protected boolean validateExpiration(SignedJWT jwtToken) { - boolean valid = false; - - try { - Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); - - if (expires == null || new Date().before(expires)) { - LOG.debug("SSO token expiration date has been successfully validated"); - - valid = true; - } else { - LOG.warn("SSO expiration date validation failed."); - } - } catch (ParseException pe) { - LOG.warn("SSO expiration date validation failed.", pe); - } - - return valid; - } - - protected boolean validateAudiences(SignedJWT jwtToken) { - boolean valid = false; - - if (jwtProperties.getAudiences().isEmpty()) { - // if there were no expected audiences configured then just - // consider any audience acceptable - valid = true; - } else { - try { - List tokenAudienceList = jwtToken.getJWTClaimsSet().getAudience(); - - // if any of the configured audiences is found then consider it acceptable - if (tokenAudienceList != null) { - for (String aud : tokenAudienceList) { - if (jwtProperties.getAudiences().contains(aud)) { - LOG.debug("Audience claim has been validated."); - - valid = true; - - break; - } - } - } - } catch (ParseException pe) { - LOG.warn("Audience validation failed.", pe); - } - } - - return valid; - } - - /** - * Create the redirect URL to be used for authentication of the user in the absence - * of a JWT token within the incoming request. - * - * @param request for getting the original request URL - * @return url to use as login url for redirect - */ - protected String constructLoginURLForApi(HttpServletRequest request, String xForwardedURL) { - String delimiter = "?"; - - if (authenticationProviderUrl.contains("?")) { - delimiter = "&"; - } - - String loginURL = authenticationProviderUrl + delimiter + originalUrlQueryParam + "="; - - if (StringUtils.trimToNull(xForwardedURL) != null) { - loginURL += xForwardedURL; - } else { - loginURL += request.getRequestURL(); - } - - if (StringUtils.isNotEmpty(request.getRequestURI()) && request.getRequestURI().length() > 1) { - loginURL = loginURL.replace(request.getRequestURI(), "/"); - } - - return loginURL; - } - - protected boolean isBrowserAgent(String userAgent) { - boolean isWeb = false; - String agents = PropertiesUtil.getProperty("ranger.krb.browser-useragents-regex", RangerCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT); - - if (agents == null) { - agents = RangerCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT; - } - - String[] browserUserAgents = agents.split(","); - - if (browserUserAgents.length > 0 && userAgent != null) { - for (String ua : browserUserAgents) { - if (userAgent.toLowerCase().startsWith(ua.toLowerCase())) { - isWeb = true; - break; - } - } - } - - return isWeb; - } - - private Authentication getGrantedAuthority(Authentication authentication) { - if (authentication != null && authentication.isAuthenticated()) { - final List grantedAuths = getAuthorities(authentication.getName()); - final UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString(), grantedAuths); - UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), grantedAuths); - - result.setDetails(authentication.getDetails()); - - return result; - } - - return authentication; - } - - private List getAuthorities(String username) { - Collection roleList = userMgr.getRolesByLoginId(username); - final List grantedAuths = new ArrayList<>(); - - for (String role : roleList) { - grantedAuths.add(new SimpleGrantedAuthority(role)); - } - - return grantedAuths; - } - - /* - * public static RSAPublicKey getPublicKeyFromFile(String filePath) throws - * IOException, CertificateException { - * FileUtils.readFileToString(new File(filePath)); - * getPublicKeyFromString(pemString); } - */ - - private boolean isWebUserAgent(String userAgent) { - boolean isWeb = false; - - if (jwtProperties != null) { - String[] userAgentList = jwtProperties.getUserAgentList(); - - if (userAgentList != null) { - for (String ua : userAgentList) { - if (userAgent.toLowerCase().startsWith(ua.toLowerCase())) { - isWeb = true; - break; - } - } - } - } - - return isWeb; - } - - private void loadJwtProperties() { - if (jwtProperties != null) { - authenticationProviderUrl = jwtProperties.getAuthenticationProviderUrl(); - publicKey = jwtProperties.getPublicKey(); - cookieName = jwtProperties.getCookieName(); - originalUrlQueryParam = jwtProperties.getOriginalUrlQueryParam(); - } - } - - /** - * Do not try to validate JWT if user already authenticated via other - * provider - * - * @return true, if JWT validation required - */ - private boolean isAuthenticated() { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - - return !(!(existingAuth != null && existingAuth.isAuthenticated()) || existingAuth instanceof SSOAuthentication); - } -} diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSecurityContextFormationFilter.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSecurityContextFormationFilter.java index cf03c6cbdbb..a1ba39e7878 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSecurityContextFormationFilter.java +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSecurityContextFormationFilter.java @@ -172,10 +172,8 @@ private int getAuthType(Authentication auth, HttpServletRequest request) { return ((RangerAuthenticationToken) auth).getAuthType(); } - Object ssoEnabledObj = request.getAttribute("ssoEnabled"); - boolean ssoEnabled = ssoEnabledObj != null ? Boolean.parseBoolean(String.valueOf(ssoEnabledObj)) : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false); - - if (ssoEnabled) { + // JWT/Bearer-token logins are recorded as SSO for audit continuity (marker set by RangerJwtAuthFilter). + if (request.getAttribute("jwtAuthenticated") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("jwtAuthenticated")))) { return XXAuthSession.AUTH_TYPE_SSO; } else if (request.getAttribute("spnegoEnabled") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("spnegoEnabled")))) { if (request.getAttribute("trustedProxyEnabled") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("trustedProxyEnabled")))) { diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthentication.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthentication.java deleted file mode 100644 index 696a55ff97a..00000000000 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthentication.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import com.nimbusds.jwt.SignedJWT; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; - -/** - * Internal token which describes JWT authentication - */ -public class SSOAuthentication implements Authentication { - private final SignedJWT token; - private boolean authenticated; - - public SSOAuthentication(SignedJWT token) { - this.token = token; - } - - @Override - public String getName() { - return null; - } - - @Override - public Collection getAuthorities() { - return null; - } - - @Override - public SignedJWT getCredentials() { - return token; - } - - @Override - public Object getDetails() { - return null; - } - - @Override - public Object getPrincipal() { - return null; - } - - @Override - public boolean isAuthenticated() { - return authenticated; - } - - @Override - public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { - this.authenticated = authenticated; - } -} diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthenticationProperties.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthenticationProperties.java deleted file mode 100644 index 088850a994e..00000000000 --- a/security-admin/src/main/java/org/apache/ranger/security/web/filter/SSOAuthenticationProperties.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import java.security.interfaces.RSAPublicKey; -import java.util.Collections; -import java.util.List; - -public class SSOAuthenticationProperties { - private String authenticationProviderUrl; - private RSAPublicKey publicKey; - private String cookieName = "hadoop-jwt"; - private String originalUrlQueryParam; - private String[] userAgentList; - private List audiences = Collections.emptyList(); - private String expectedSigAlg; - - public String getAuthenticationProviderUrl() { - return authenticationProviderUrl; - } - - public void setAuthenticationProviderUrl(String authenticationProviderUrl) { - this.authenticationProviderUrl = authenticationProviderUrl; - } - - public RSAPublicKey getPublicKey() { - return publicKey; - } - - public void setPublicKey(RSAPublicKey publicKey) { - this.publicKey = publicKey; - } - - public String getCookieName() { - return cookieName; - } - - public void setCookieName(String cookieName) { - this.cookieName = cookieName; - } - - public String getOriginalUrlQueryParam() { - return originalUrlQueryParam; - } - - public void setOriginalUrlQueryParam(String originalUrlQueryParam) { - this.originalUrlQueryParam = originalUrlQueryParam; - } - - /** - * @return the userAgentList - */ - public String[] getUserAgentList() { - return userAgentList; - } - - /** - * @param userAgentList the userAgentList to set - */ - public void setUserAgentList(String[] userAgentList) { - this.userAgentList = userAgentList; - } - - public List getAudiences() { - return audiences; - } - - public void setAudiences(List audiences) { - this.audiences = audiences; - } - - public String getExpectedSigAlg() { - return expectedSigAlg; - } - - public void setExpectedSigAlg(String expectedSigAlg) { - this.expectedSigAlg = expectedSigAlg; - } -} diff --git a/security-admin/src/main/java/org/apache/ranger/util/RestUtil.java b/security-admin/src/main/java/org/apache/ranger/util/RestUtil.java index c097f6fb0e0..a28b986a88d 100644 --- a/security-admin/src/main/java/org/apache/ranger/util/RestUtil.java +++ b/security-admin/src/main/java/org/apache/ranger/util/RestUtil.java @@ -40,7 +40,6 @@ public class RestUtil { public static final String timeOffsetCookieName = "clientTimeOffset"; public static final String TIMEOUT_ACTION = "timeout"; - public static final String LOCAL_LOGIN_URL = "locallogin"; public static final String ZONED_EVENT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss z"; private static final String PROXY_RANGER_URL_PATH = "/ranger"; diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml index 164b63d027a..36ec20305f8 100644 --- a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml +++ b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml @@ -594,14 +594,6 @@ - - ranger.sso.cookiename - hadoop-jwt - - - ranger.sso.query.param.originalurl - originalUrl - ranger.rest-csrf.enabled true diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml index d327f316712..0654902e810 100644 --- a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml +++ b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml @@ -296,20 +296,19 @@ (sAMAccountName={0}) - + - ranger.sso.providerurl - https://127.0.0.1:8443/gateway/knoxsso/api/v1/websso + ranger.admin.jwt.providerurl + - ranger.sso.enabled - false - + ranger.admin.jwt.publickey + + - ranger.sso.browser.useragent + ranger.admin.jwt.browser.useragent Mozilla,chrome - ranger.admin.healthcheck.username healthcheck diff --git a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml index b99a5537c78..9ec89ca2245 100644 --- a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml +++ b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml @@ -66,7 +66,6 @@ http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd"> - @@ -120,9 +119,6 @@ http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd"> - - - {/* USER PROFILE */} } /> - {/* KNOX SSO WARNING */} - } - /> {/*DATA NOT FOUND PAGE*/} } /> - } /> {/* NOT FOUND ROUTE */} {/* GDS */} diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js index 34df921d4ce..2350afb5e27 100755 --- a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js +++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js @@ -687,10 +687,8 @@ export const PathAssociateWithModule = { ], Permission: ["/permissions/models", "/permissions/:permissionId/edit"], Profile: ["/userprofile"], - KnoxSignOut: ["/knoxSSOWarning"], DataNotFound: ["/dataNotFound"], PageNotFound: ["/pageNotFound"], - localLogin: ["/locallogin"], slashPath: ["/"], Forbidden: ["/forbidden"], CreateNewPolicyForm: ["/policymanager/create-policy"], diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js index 438caaf52b6..b17ca7f17ab 100644 --- a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js +++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js @@ -163,12 +163,10 @@ export const hasAccessToPath = (pathName) => { let groupModules = map(userProfile?.groupPermissions, "moduleName"); let moduleNames = union(userModules, groupModules); moduleNames.push("Profile"); - moduleNames.push("KnoxSignOut"); moduleNames.push("DataNotFound"); moduleNames.push("PageNotFound"); moduleNames.push("Forbidden"); - moduleNames.push("localLogin"); if (isSystemAdmin() || isAuditor()) { moduleNames.push("Permission"); } @@ -1427,7 +1425,7 @@ export const updateTagActive = (isTagView) => { } }; -export const handleLogout = async (checkKnoxSSOVal, navigate) => { +export const handleLogout = async () => { try { await fetchApi({ url: "logout", @@ -1436,48 +1434,13 @@ export const handleLogout = async (checkKnoxSSOVal, navigate) => { "cache-control": "no-cache" } }); - if (checkKnoxSSOVal !== undefined || checkKnoxSSOVal !== null) { - if (checkKnoxSSOVal?.toString() == "false") { - window.location.replace("/locallogin"); - window.localStorage.clear(); - } else { - navigate("/knoxSSOWarning"); - } - } else { - window.location.replace("login.jsp"); - } + window.localStorage.clear(); + window.location.replace("login.jsp"); } catch (error) { toast.error(`Error occurred while logout! ${error}`); } }; -export const checkKnoxSSO = async (navigate) => { - const userProfile = getUserProfile(); - let checkKnoxSSOresp = {}; - try { - checkKnoxSSOresp = await fetchApi({ - url: "plugins/checksso", - type: "GET", - headers: { - "cache-control": "no-cache" - } - }); - if ( - checkKnoxSSOresp?.data?.toString() == "true" && - userProfile?.configProperties?.inactivityTimeout > 0 - ) { - window.location.replace("index.html?action=timeout"); - } else { - handleLogout(checkKnoxSSOresp?.data, navigate); - } - } catch (error) { - if (checkKnoxSSOresp?.status == "419") { - window.location.replace("login.jsp"); - } - console.error(`Error occurred while logout! ${error}`); - } -}; - export const navigateTo = { navigate: null }; diff --git a/security-admin/src/main/webapp/react-webapp/src/views/ErrorPage.jsx b/security-admin/src/main/webapp/react-webapp/src/views/ErrorPage.jsx index f14b1623708..439069f4511 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/ErrorPage.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/ErrorPage.jsx @@ -49,18 +49,6 @@ export const ErrorPage = (props) => { "Sorry, you don't have enough privileges to view this page." ); } - if (props.errorCode == "checkSSOTrue") { - setErrorCode("Sign Out Is Not Complete!"); - setErrorInfo( - - Authentication to this instance of Ranger is managed externally(for - example,Apache Knox). You can still open this instance of Ranger from - the same web browser without re-authentication.To prevent additional - access to Ranger, - close all browser windows and exit the browser. - - ); - } if (props.errorCode == "404") { setErrorCode("Page not found (404)."); setErrorInfo("Sorry, this page isn't here or has moved."); @@ -97,11 +85,9 @@ export const ErrorPage = (props) => { > Go back - {props.errorCode !== "checkSSOTrue" && ( - - )} + diff --git a/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx b/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx index d6199b41f59..444df798c79 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx @@ -29,7 +29,7 @@ import { import ErrorPage from "./ErrorPage"; import { hasAccessToPath, - checkKnoxSSO, + handleLogout, navigateTo, getLandingPageURl } from "Utils/XAUtils"; @@ -44,7 +44,6 @@ import dateFormat from "dateformat"; const Layout = () => { let location = useLocation(); - const navigate = useNavigate(); const userProfile = getUserProfile(); navigateTo.navigate = useNavigate(); const [open, setOpen] = useState(false); @@ -57,9 +56,9 @@ const Layout = () => { : 900); const promptTimeout = 1000 * 15; - const handleLogout = async () => { + const onLogout = async () => { setOpen(false); - checkKnoxSSO(navigate); + handleLogout(); }; const onPrompt = () => { @@ -69,7 +68,7 @@ const Layout = () => { const onIdle = () => { setOpen(false); - handleLogout(); + onLogout(); setTimer(0); }; @@ -132,7 +131,7 @@ const Layout = () => { - @@ -142,7 +141,6 @@ const Layout = () => { {location.pathname === "/" && - window.location.pathname !== "/locallogin" && window.location.pathname != "/dataNotFound" && window.location.pathname != "/pageNotFound" && window.location.pathname != "/forbidden" && ( diff --git a/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBarBody.jsx b/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBarBody.jsx index a8e87f8ab39..e4f54674d01 100644 --- a/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBarBody.jsx +++ b/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBarBody.jsx @@ -18,10 +18,10 @@ */ import React, { useReducer } from "react"; -import { NavLink, useNavigate } from "react-router-dom"; +import { NavLink } from "react-router-dom"; import { sortBy, filter, isEmpty, map, upperCase, groupBy } from "lodash"; import closeIcon from "Images/close.svg"; -import { getUserProfile, setUserProfile } from "Utils/appState"; +import { setUserProfile } from "Utils/appState"; import { fetchApi } from "Utils/fetchAPI"; import Select from "react-select"; import { @@ -68,7 +68,6 @@ export const SideBarBody = (props) => { const { selectedServiceDef } = keyState; const isKMSRole = isKeyAdmin() || isKMSAuditor(); - const navigate = useNavigate(); const apiUrl = getBaseUrl() + "apidocs/swagger.html"; const serviceSelectThemes = (theme) => { @@ -196,36 +195,8 @@ export const SideBarBody = (props) => { }); }; - const checkKnoxSSO = async (e) => { + const handleLogout = async (e) => { e.preventDefault(); - const userProfile = getUserProfile(); - let checkKnoxSSOresp = {}; - try { - checkKnoxSSOresp = await fetchApi({ - url: "plugins/checksso", - type: "GET", - headers: { - "cache-control": "no-cache" - } - }); - if ( - checkKnoxSSOresp?.data?.toString() == "true" && - userProfile?.configProperties?.inactivityTimeout > 0 - ) { - window.location.replace("index.html?action=timeout"); - } else { - handleLogout(checkKnoxSSOresp?.data); - } - } catch (error) { - if (checkKnoxSSOresp?.status == "419") { - setUserProfile(null); - window.location.replace("login.jsp"); - } - console.error(`Error occurred while logout! ${error}`); - } - }; - - const handleLogout = async (checkKnoxSSOVal) => { try { await fetchApi({ url: "logout", @@ -234,17 +205,9 @@ export const SideBarBody = (props) => { "cache-control": "no-cache" } }); - if (checkKnoxSSOVal !== undefined || checkKnoxSSOVal !== null) { - if (checkKnoxSSOVal?.toString() == "false") { - window.location.replace("locallogin"); - window.localStorage.clear(); - setUserProfile(null); - } else { - navigate("/knoxSSOWarning"); - } - } else { - window.location.replace("login.jsp"); - } + window.localStorage.clear(); + setUserProfile(null); + window.location.replace("login.jsp"); } catch (error) { toast.error(`Error occurred while logout! ${error}`); } @@ -672,7 +635,7 @@ export const SideBarBody = (props) => {
  • diff --git a/security-admin/src/test/java/org/apache/ranger/biz/TestRangerBizUtil.java b/security-admin/src/test/java/org/apache/ranger/biz/TestRangerBizUtil.java index bc94001ef5b..9c224e3cc08 100644 --- a/security-admin/src/test/java/org/apache/ranger/biz/TestRangerBizUtil.java +++ b/security-admin/src/test/java/org/apache/ranger/biz/TestRangerBizUtil.java @@ -850,11 +850,7 @@ public void testHasKMSPermissions_ThrowsForKeyAdminOnNonKMS_andSysAdminOnKMSDef( } @Test - public void testIsSSOEnabled_andGetDBVersion_andUserAdminChecks() throws Exception { - // session exists, SSO from session value - RangerContextHolder.getSecurityContext().getUserSession().setSSOEnabled(Boolean.TRUE); - Assertions.assertTrue(rangerBizUtil.isSSOEnabled()); - + public void testGetDBVersion_andUserAdminChecks() throws Exception { // DB version path XXUserDao xxUserDao = Mockito.mock(XXUserDao.class); Mockito.when(daoManager.getXXUser()).thenReturn(xxUserDao); diff --git a/security-admin/src/test/java/org/apache/ranger/biz/TestSessionMgr.java b/security-admin/src/test/java/org/apache/ranger/biz/TestSessionMgr.java index 68033907b3f..22602b894d3 100644 --- a/security-admin/src/test/java/org/apache/ranger/biz/TestSessionMgr.java +++ b/security-admin/src/test/java/org/apache/ranger/biz/TestSessionMgr.java @@ -166,6 +166,57 @@ public void testStoreAuthSessionPersistsRegularUser() { assertEquals(created, ret); } + @Test + public void testProcessSuccessLogin_CreatesHealthCheckUserWhenSsoDisabled() { + Authentication authentication = mock(Authentication.class); + WebAuthenticationDetails details = mock(WebAuthenticationDetails.class); + when(authentication.getDetails()).thenReturn(details); + when(authentication.getName()).thenReturn(RangerBizUtil.HEALTHCHECK_USERNAME); + SecurityContext sc = SecurityContextHolder.createEmptyContext(); + sc.setAuthentication(authentication); + SecurityContextHolder.setContext(sc); + + XXPortalUser healthUser = new XXPortalUser(); + healthUser.setId(42L); + healthUser.setLoginId(RangerBizUtil.HEALTHCHECK_USERNAME); + + XXPortalUserDao portalDao = mock(XXPortalUserDao.class); + when(daoManager.getXXPortalUser()).thenReturn(portalDao); + when(portalDao.findByLoginId(RangerBizUtil.HEALTHCHECK_USERNAME)).thenReturn(null, healthUser); + + when(bizUtil.isHealthCheckUser(RangerBizUtil.HEALTHCHECK_USERNAME)).thenReturn(true); + + XXPortalUserRoleDao roleDao = mock(XXPortalUserRoleDao.class); + when(daoManager.getXXPortalUserRole()).thenReturn(roleDao); + when(roleDao.findByUserId(42L)).thenReturn(Collections.emptyList()); + + XXUserDao xxUserDao = mock(XXUserDao.class); + when(daoManager.getXXUser()).thenReturn(xxUserDao); + XXUser xUser = new XXUser(); + xUser.setId(42L); + when(xxUserDao.findByUserName(RangerBizUtil.HEALTHCHECK_USERNAME)).thenReturn(xUser); + + XXModuleDefDao moduleDefDao = mock(XXModuleDefDao.class); + when(daoManager.getXXModuleDef()).thenReturn(moduleDefDao); + when(moduleDefDao.findAccessibleModulesByUserId(42L, 42L)).thenReturn(Collections.emptyList()); + + when(httpUtil.getDeviceType(anyString())).thenReturn(RangerCommonEnums.DEVICE_UNKNOWN); + + HttpSession httpSession = mock(HttpSession.class); + when(httpSession.getAttribute("auditLoginId")).thenReturn(null); + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getSession()).thenReturn(httpSession); + when(request.getRequestURI()).thenReturn("/service/actuator/health/readiness"); + + UserSessionBase ret = sessionMgr.processSuccessLogin(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY, "probe", request); + + verify(xUserMgr).createServiceConfigUserSynchronously(RangerBizUtil.HEALTHCHECK_USERNAME); + verify(xUserMgr, never()).createServiceConfigUser(anyString()); + assertNotNull(ret); + + SecurityContextHolder.clearContext(); + } + @Test public void testProcessSuccessLogin_ExistingValidSession() { // Prepare existing user session in RangerContextHolder @@ -248,7 +299,6 @@ public void testProcessSuccessLogin_CreateNewSession() { UserSessionBase ret = sessionMgr.processSuccessLogin(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY, "UA", request); assertNotNull(ret); assertEquals(21L, ret.getUserId()); - assertTrue(ret.isSSOEnabled()); assertTrue(ret.isSpnegoEnabled()); } diff --git a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java index 821d1a2e413..00abb434b57 100644 --- a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java +++ b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java @@ -2643,17 +2643,6 @@ public void test83GetServiceHeadersWithFiltering() throws Exception { Assertions.assertEquals(0, result.size()); } - @Test - public void test84CheckSSO() throws Exception { - Mockito.when(bizUtil.isSSOEnabled()).thenReturn(true); - String result = serviceREST.checkSSO(); - Assertions.assertEquals("true", result); - - Mockito.when(bizUtil.isSSOEnabled()).thenReturn(false); - result = serviceREST.checkSSO(); - Assertions.assertEquals("false", result); - } - @Test public void test85GetCSRFProperties() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -3799,18 +3788,6 @@ public void test148GetServicePoliciesByIdWithException() throws Exception { Mockito.verify(restErrorUtil).createRESTException("Service not found"); } - @Test - public void test149CheckSSOWithDifferentValues() throws Exception { - Mockito.when(bizUtil.isSSOEnabled()).thenReturn(true); - String result = serviceREST.checkSSO(); - Assertions.assertEquals("true", result); - - Mockito.reset(bizUtil); - Mockito.when(bizUtil.isSSOEnabled()).thenReturn(false); - result = serviceREST.checkSSO(); - Assertions.assertEquals("false", result); - } - @Test public void test150GetCSRFPropertiesWithNullSession() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); diff --git a/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java b/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java index 33145329f9f..41d69f15a22 100644 --- a/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java +++ b/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java @@ -154,17 +154,6 @@ public void settersAndGetters_work() { LdapAuthenticator authenticator = mock(LdapAuthenticator.class); provider.setAuthenticator(authenticator); assertSame(authenticator, provider.getAuthenticator()); - - provider.setSsoEnabled(true); - assertTrue(provider.isSsoEnabled()); - } - - @Test - public void ssoEnabled_authenticateReturnsSameToken() { - provider.setSsoEnabled(true); - UsernamePasswordAuthenticationToken input = new UsernamePasswordAuthenticationToken("u", "p"); - Authentication result = provider.authenticate(input); - assertSame(input, result); } @Test diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/authentication/TestRangerAuthenticationEntryPoint.java b/security-admin/src/test/java/org/apache/ranger/security/web/authentication/TestRangerAuthenticationEntryPoint.java index 9bc2c4e16d2..f2d569c64e6 100644 --- a/security-admin/src/test/java/org/apache/ranger/security/web/authentication/TestRangerAuthenticationEntryPoint.java +++ b/security-admin/src/test/java/org/apache/ranger/security/web/authentication/TestRangerAuthenticationEntryPoint.java @@ -116,20 +116,17 @@ public void testCommence_AjaxHeaderIgnoreCase_triggersSendErrorAfterJson() throw } @Test - public void testCommence_NonAjax_LocalLogin_setsAttributesAndCallsParent() throws IOException, ServletException { + public void testCommence_NonAjax_NotService_doesNotSetLocalLoginAttributes() throws IOException, ServletException { StringWriter localBuffer = new StringWriter(); when(response.getWriter()).thenReturn(new PrintWriter(localBuffer)); PropertiesUtil.getPropertiesMap().put("ranger.servlet.mapping.url.pattern", "service"); HttpSession session = Mockito.mock(HttpSession.class); ServletContext context = Mockito.mock(ServletContext.class); - when(request.getSession()).thenReturn(session); - when(request.getServletContext()).thenReturn(context); when(request.getContextPath()).thenReturn(""); when(request.getScheme()).thenReturn("http"); when(request.getServerName()).thenReturn("localhost"); when(request.getServerPort()).thenReturn(80); - when(session.getId()).thenReturn("sid"); when(request.getHeader("X-Requested-With")).thenReturn(null); when(request.getRequestURI()).thenReturn("/locallogin"); @@ -137,8 +134,9 @@ public void testCommence_NonAjax_LocalLogin_setsAttributesAndCallsParent() throw entryPoint.commence(request, response, authException); - verify(session).setAttribute("locallogin", "true"); - verify(context).setAttribute("sid", "locallogin"); + // Knox SSO "locallogin" scaffolding has been removed; no locallogin session/context attributes should be set. + verify(session, never()).setAttribute(anyString(), anyString()); + verify(context, never()).setAttribute(anyString(), anyString()); } @Test diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthFilter.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthFilter.java index 50d18ab2e13..9a2d8123136 100644 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthFilter.java +++ b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthFilter.java @@ -77,6 +77,30 @@ public void testDestroy_noopDoesNotThrow() { assertDoesNotThrow(() -> filter.destroy()); } + @Test + public void testInitialize_doesNotThrowWhenJwtConfigMissing() { + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PROVIDER_URL); + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PUBLIC_KEY); + + RangerJwtAuthFilter filter = new RangerJwtAuthFilter(); + assertDoesNotThrow(() -> filter.initialize()); + + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PROVIDER_URL); + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PUBLIC_KEY); + } + + @Test + public void testInitialize_doesNotThrowWhenPublicKeyEmptyAndProviderUrlSet() { + PropertiesUtil.getPropertiesMap().put(RangerJwtAuthConfig.PROVIDER_URL, "https://knox.example/gateway/knoxsso/api/v1/jwks.json"); + PropertiesUtil.getPropertiesMap().put(RangerJwtAuthConfig.PUBLIC_KEY, ""); + + RangerJwtAuthFilter filter = new RangerJwtAuthFilter(); + assertDoesNotThrow(() -> filter.initialize()); + + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PROVIDER_URL); + PropertiesUtil.getPropertiesMap().remove(RangerJwtAuthConfig.PUBLIC_KEY); + } + @Test public void testInitialize_doesNotThrow() { RangerJwtAuthFilter filter = new RangerJwtAuthFilter(); diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthWrapper.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthWrapper.java index 4cc49b91c31..b2cfc2686d0 100644 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthWrapper.java +++ b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerJwtAuthWrapper.java @@ -18,10 +18,7 @@ */ package org.apache.ranger.security.web.filter; -import org.apache.ranger.common.PropertiesUtil; -import org.apache.ranger.common.UserSessionBase; import org.apache.ranger.security.context.RangerContextHolder; -import org.apache.ranger.security.context.RangerSecurityContext; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; @@ -64,27 +61,18 @@ public void tearDown() { SecurityContextHolder.clearContext(); RangerContextHolder.resetSecurityContext(); RangerContextHolder.resetOpContext(); - PropertiesUtil.getPropertiesMap().remove("ranger.sso.enabled"); } @Test public void testDoFilter_redirectsToLoginForBrowserWhenNotAuthenticated() throws IOException, ServletException { System.setProperty("ranger.default.browser-useragents", "Mozilla,Chrome,Opera"); SecurityContextHolder.clearContext(); - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); RangerJwtAuthWrapper wrapper = Mockito.spy(new RangerJwtAuthWrapper()); wrapper.initialize(); RangerJwtAuthFilter jwtFilter = Mockito.mock(RangerJwtAuthFilter.class); wrapper.rangerJwtAuthFilter = jwtFilter; - // Set context with SSO disabled - RangerSecurityContext context = new RangerSecurityContext(); - UserSessionBase sessionBase = new UserSessionBase(); - sessionBase.setSSOEnabled(Boolean.FALSE); - context.setUserSession(sessionBase); - RangerContextHolder.setSecurityContext(context); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); FilterChain chain = Mockito.mock(FilterChain.class); @@ -104,7 +92,7 @@ public void testDoFilter_redirectsToLoginForBrowserWhenNotAuthenticated() throws } @Test - public void testDoFilter_skipsJwtWhenSsoEnabled() throws IOException, ServletException { + public void testDoFilter_invokesJwtWhenBearerPresent() throws IOException, ServletException { System.setProperty("ranger.default.browser-useragents", "Mozilla,Chrome,Opera"); RangerJwtAuthWrapper wrapper = Mockito.spy(new RangerJwtAuthWrapper()); @@ -112,20 +100,15 @@ public void testDoFilter_skipsJwtWhenSsoEnabled() throws IOException, ServletExc RangerJwtAuthFilter jwtFilter = Mockito.mock(RangerJwtAuthFilter.class); wrapper.rangerJwtAuthFilter = jwtFilter; - // SSO enabled in context - RangerSecurityContext context = new RangerSecurityContext(); - UserSessionBase sessionBase = new UserSessionBase(); - sessionBase.setSSOEnabled(Boolean.TRUE); - context.setUserSession(sessionBase); - RangerContextHolder.setSecurityContext(context); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); FilterChain chain = Mockito.mock(FilterChain.class); + when(req.getHeader("Authorization")).thenReturn("Bearer sometoken"); + wrapper.doFilter(req, res, chain); - verify(jwtFilter, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class), any(FilterChain.class)); + verify(jwtFilter, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class), any(FilterChain.class)); verify(chain, times(1)).doFilter(req, res); } @@ -133,7 +116,6 @@ public void testDoFilter_skipsJwtWhenSsoEnabled() throws IOException, ServletExc void testDoFilter_invokesJwtFilter_whenBearerHeaderPresent() throws Exception { RangerContextHolder.resetSecurityContext(); SecurityContextHolder.clearContext(); - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); @@ -154,8 +136,6 @@ void testDoFilter_invokesJwtFilter_whenBearerHeaderPresent() throws Exception { @Test void testDoFilter_skipsJwt_whenAlreadyAuthenticated_evenIfBearerHeaderPresent() throws Exception { - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); - // mark request authenticated SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken( @@ -178,8 +158,6 @@ void testDoFilter_skipsJwt_whenAlreadyAuthenticated_evenIfBearerHeaderPresent() @Test void testDoFilter_skipsJwtFilter_whenNoBearer() throws Exception { - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); FilterChain chain = Mockito.mock(FilterChain.class); @@ -197,9 +175,27 @@ void testDoFilter_skipsJwtFilter_whenNoBearer() throws Exception { verify(chain, times(1)).doFilter(req, res); } + @Test + void testDoFilter_skipsJwtForHealthCheckPath_evenWithBearerHeader() throws Exception { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + HttpServletResponse res = Mockito.mock(HttpServletResponse.class); + FilterChain chain = Mockito.mock(FilterChain.class); + + Mockito.when(req.getRequestURI()).thenReturn("/service/actuator/health/readiness"); + Mockito.when(req.getHeader("Authorization")).thenReturn("Bearer token"); + + RangerJwtAuthFilter jwt = Mockito.mock(RangerJwtAuthFilter.class); + RangerJwtAuthWrapper wrapper = new RangerJwtAuthWrapper(); + setField(wrapper, "rangerJwtAuthFilter", jwt); + + wrapper.doFilter(req, res, chain); + + verify(jwt, never()).doFilter(req, res, chain); + verify(chain, times(1)).doFilter(req, res); + } + @Test void testDoFilter_redirectsToLogin_whenJwtAttemptedButUnauthenticated_andBrowserAgent() throws Exception { - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); System.setProperty("ranger.default.browser-useragents", "Mozilla"); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSSOAuthenticationFilter.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSSOAuthenticationFilter.java deleted file mode 100644 index 38459cd6691..00000000000 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSSOAuthenticationFilter.java +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSObject; -import com.nimbusds.jose.util.Base64URL; -import com.nimbusds.jwt.SignedJWT; -import org.apache.ranger.biz.UserMgr; -import org.apache.ranger.common.PropertiesUtil; -import org.apache.ranger.common.UserSessionBase; -import org.apache.ranger.security.context.RangerContextHolder; -import org.apache.ranger.security.context.RangerSecurityContext; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.User; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.security.interfaces.RSAPublicKey; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Vector; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * @generated by Cursor - * @description - */ -@TestMethodOrder(MethodOrderer.MethodName.class) -@ExtendWith(MockitoExtension.class) -public class TestRangerSSOAuthenticationFilter { - private SSOAuthenticationProperties makeProps() { - SSOAuthenticationProperties p = new SSOAuthenticationProperties(); - p.setAuthenticationProviderUrl("https://knox/gateway/knoxsso/api/v1/websso"); - p.setCookieName("hadoop-jwt"); - p.setOriginalUrlQueryParam("originalUrl"); - p.setUserAgentList(new String[] {"Mozilla", "Chrome" }); - p.setAudiences(Arrays.asList("ranger-admin")); - p.setExpectedSigAlg("RS256"); - return p; - } - - @Test - public void testConstructorsAndInitDestroy() throws Exception { - RangerSSOAuthenticationFilter filter1 = new RangerSSOAuthenticationFilter(); - RangerSSOAuthenticationFilter filter2 = new RangerSSOAuthenticationFilter(makeProps()); - assertNotNull(filter1); - assertNotNull(filter2); - - FilterConfig cfg = Mockito.mock(FilterConfig.class); - assertDoesNotThrow(() -> filter1.init(cfg)); - assertDoesNotThrow(filter1::destroy); - } - - @Test - public void testParseRSAPublicKey_throwsOnInvalid() { - assertThrows(ServletException.class, () -> RangerSSOAuthenticationFilter.parseRSAPublicKey("invalidpem")); - } - - @Test - public void testGetJwtProperties_nullWhenDisabledOrMissing() { - PropertiesUtil.getPropertiesMap().clear(); - PropertiesUtil.getPropertiesMap().put("ranger.sso.providerurl", "https://knox/sso"); - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "false"); - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(); - assertNull(filter.getJwtProperties()); - } - - @Test - public void testGetJwtProperties_presentWhenEnabled() { - PropertiesUtil.getPropertiesMap().clear(); - PropertiesUtil.getPropertiesMap().put("ranger.sso.providerurl", "https://knox/sso"); - PropertiesUtil.getPropertiesMap().put("ranger.sso.enabled", "true"); - PropertiesUtil.getPropertiesMap().put("ranger.sso.publicKey", "invalid"); // will log error, still returns props - PropertiesUtil.getPropertiesMap().put("ranger.sso.cookiename", "hadoop-jwt"); - PropertiesUtil.getPropertiesMap().put("ranger.sso.query.param.originalurl", "originalUrl"); - SSOAuthenticationProperties props = new RangerSSOAuthenticationFilter().getJwtProperties(); - assertNotNull(props); - assertEquals("hadoop-jwt", props.getCookieName()); - assertEquals("originalUrl", props.getOriginalUrlQueryParam()); - } - - @Test - public void testGetJWTFromCookie_findsCookie() { - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(makeProps()); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - Cookie jwt = new Cookie("hadoop-jwt", "token123"); - when(req.getCookies()).thenReturn(new Cookie[] {jwt}); - assertEquals("token123", filter.getJWTFromCookie(req)); - } - - @Test - public void testValidateToken_chaining() throws Exception { - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(makeProps()) { - boolean isSignatureValid; - boolean isExpirationValid; - boolean isAudienceValid; - - @Override - protected boolean validateSignature(SignedJWT t) { - return isSignatureValid; - } - - @Override - protected boolean validateExpiration(SignedJWT t) { - return isExpirationValid; - } - - @Override - protected boolean validateAudiences(SignedJWT t) { - return isAudienceValid; - } - }; - SignedJWT token = Mockito.mock(SignedJWT.class); - - // signature false -> false - setBooleanFields(filter, false, false, false); - assertFalse(filter.validateToken(token)); - // signature true but expiration false -> false - setBooleanFields(filter, true, false, false); - assertFalse(filter.validateToken(token)); - // sig+exp true but audience false -> false - setBooleanFields(filter, true, true, false); - assertFalse(filter.validateToken(token)); - // all true -> true - setBooleanFields(filter, true, true, true); - assertTrue(filter.validateToken(token)); - } - - private void setBooleanFields(RangerSSOAuthenticationFilter f, boolean s, boolean e, boolean a) throws Exception { - // using reflection to set the fields in the anonymous subclass - Field sf = f.getClass().getDeclaredField("isSignatureValid"); - sf.setAccessible(true); - sf.setBoolean(f, s); - Field ef = f.getClass().getDeclaredField("isExpirationValid"); - ef.setAccessible(true); - ef.setBoolean(f, e); - Field af = f.getClass().getDeclaredField("isAudienceValid"); - af.setAccessible(true); - af.setBoolean(f, a); - } - - @Test - public void testValidateSignature_handlesExceptionsAndAlgMismatch() throws Exception { - SSOAuthenticationProperties props = makeProps(); - props.setExpectedSigAlg("ES256"); - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(props); - // Case 1: publicKey null, verify path throws inside -> false - SignedJWT parsed = SignedJWT.parse("eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhIn0.signature"); - assertFalse(f.validateSignature(parsed)); - - // Case 2: provide dummy public key and force verify true then alg mismatch -> false - RSAPublicKey dummyKey = Mockito.mock(RSAPublicKey.class); - Field pk = RangerSSOAuthenticationFilter.class.getDeclaredField("publicKey"); - pk.setAccessible(true); - pk.set(f, dummyKey); - - SignedJWT mockJwt = Mockito.mock(SignedJWT.class); - when(mockJwt.getState()).thenReturn(JWSObject.State.SIGNED); - when(mockJwt.getSignature()).thenReturn(new Base64URL("AA")); - when(mockJwt.verify(any())).thenReturn(true); - JWSHeader header = Mockito.mock(JWSHeader.class); - when(header.getAlgorithm()).thenReturn(JWSAlgorithm.RS256); - when(mockJwt.getHeader()).thenReturn(header); - assertFalse(f.validateSignature(mockJwt)); - } - - @Test - public void testValidateExpiration() throws Exception { - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - SignedJWT jwt = Mockito.mock(SignedJWT.class); - com.nimbusds.jwt.JWTClaimsSet claims = Mockito.mock(com.nimbusds.jwt.JWTClaimsSet.class); - when(jwt.getJWTClaimsSet()).thenReturn(claims); - - // null expiration -> true - when(claims.getExpirationTime()).thenReturn(null); - assertTrue(f.validateExpiration(jwt)); - - // future expiration -> true - when(claims.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 60000)); - assertTrue(f.validateExpiration(jwt)); - - // past expiration -> false - when(claims.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() - 60000)); - assertFalse(f.validateExpiration(jwt)); - } - - @Test - public void testValidateAudiences() throws Exception { - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - SignedJWT jwt = Mockito.mock(SignedJWT.class); - com.nimbusds.jwt.JWTClaimsSet claims = Mockito.mock(com.nimbusds.jwt.JWTClaimsSet.class); - when(jwt.getJWTClaimsSet()).thenReturn(claims); - - // token with matching audience -> true - when(claims.getAudience()).thenReturn(Collections.singletonList("ranger-admin")); - assertTrue(f.validateAudiences(jwt)); - - // token with non-matching audience -> false - when(claims.getAudience()).thenReturn(Collections.singletonList("other")); - assertFalse(f.validateAudiences(jwt)); - } - - @Test - public void testConstructLoginURLForApi() throws Exception { - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - when(req.getRequestURL()).thenReturn(new StringBuffer("http://localhost/app")); - when(req.getRequestURI()).thenReturn("/app"); - String url = f.constructLoginURLForApi(req, ""); - assertTrue(url.startsWith("https://knox/gateway/knoxsso/api/v1/websso")); - } - - @Test - public void testIsBrowserAgent() { - PropertiesUtil.getPropertiesMap().put("ranger.krb.browser-useragents-regex", "Mozilla,Chrome"); - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - assertTrue(f.isBrowserAgent("Mozilla/5.0")); - assertFalse(f.isBrowserAgent("curl/8.0")); - } - - @Test - public void testPrivate_isWebUserAgent_and_isAuthenticated_viaReflection() throws Exception { - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - Method isWeb = RangerSSOAuthenticationFilter.class.getDeclaredMethod("isWebUserAgent", String.class); - isWeb.setAccessible(true); - assertTrue((Boolean) isWeb.invoke(f, "Mozilla/5.0")); - assertFalse((Boolean) isWeb.invoke(f, "curl/8.0")); - - // isAuthenticated: with no auth - Method isAuth = RangerSSOAuthenticationFilter.class.getDeclaredMethod("isAuthenticated"); - isAuth.setAccessible(true); - SecurityContextHolder.clearContext(); - assertFalse((Boolean) isAuth.invoke(f)); - - // with authenticated UsernamePasswordAuthenticationToken via 3-arg constructor - SecurityContextHolder.clearContext(); - SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("u", "p", Collections.emptyList())); - assertTrue((Boolean) isAuth.invoke(f)); - - // with SSOAuthentication instance should return false - SecurityContextHolder.getContext() - .setAuthentication(new SSOAuthentication(SignedJWT.parse("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhIn0.sig"))); - assertFalse((Boolean) isAuth.invoke(f)); - } - - @Test - public void testDoFilter_localloginAttributeSkips() throws Exception { - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - ServletContext sc = Mockito.mock(ServletContext.class); - HttpSession sess = Mockito.mock(HttpSession.class); - - when(req.getRequestedSessionId()).thenReturn("sid"); - when(req.isRequestedSessionIdValid()).thenReturn(false); - when(req.getServletContext()).thenReturn(sc); - when(sc.getAttribute("sid")).thenReturn("locallogin"); - when(req.getSession()).thenReturn(sess); - doNothing().when(sess).setAttribute("locallogin", "true"); - doNothing().when(sc).removeAttribute("sid"); - - when(req.getSession()).thenReturn(sess); - when(sess.getAttribute("locallogin")).thenReturn("true"); - when(req.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); - - f.doFilter(req, res, chain); - - verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); - } - - @Test - public void testDoFilter_withValidJwtSetsAuthentication() throws Exception { - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(makeProps()); - assertNotNull(filter); - } - - @Test - public void testDoFilter_localloginUrlRedirectsWhenAuthenticated() throws Exception { - SecurityContextHolder.clearContext(); - RangerSecurityContext ctx = new RangerSecurityContext(); - UserSessionBase session = new UserSessionBase(); - session.setSSOEnabled(Boolean.TRUE); - ctx.setUserSession(session); - RangerContextHolder.setSecurityContext(ctx); - - // Set an already authenticated user - SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("bob", "", Collections.emptyList())); - - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(makeProps()); - - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - - when(req.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); - when(req.getRequestURI()).thenReturn("/locallogin/home"); - Vector headerNames = new Vector<>(); - when(req.getHeaderNames()).thenReturn(headerNames.elements()); - - filter.doFilter(req, res, chain); - - verify(res, times(1)).sendRedirect("/home"); - } - - @Test - public void testDoFilter_redirectsWhenSsoEnabledNoToken() throws Exception { - SecurityContextHolder.clearContext(); - RangerSecurityContext ctx = new RangerSecurityContext(); - UserSessionBase session = new UserSessionBase(); - session.setSSOEnabled(Boolean.TRUE); - ctx.setUserSession(session); - RangerContextHolder.setSecurityContext(ctx); - - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - - when(req.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); - when(req.getRequestURI()).thenReturn("/index.html"); - when(req.getSession()).thenReturn(Mockito.mock(HttpSession.class)); - when(req.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); - when(req.getRequestURL()).thenReturn(new StringBuffer("http://localhost/index.html")); - - f.doFilter(req, res, chain); - - verify(res, times(1)).sendRedirect(Mockito.anyString()); - } - - @Test - public void testDoFilter_ajaxSetsHeadersWhenNoToken() throws Exception { - RangerSecurityContext ctx = new RangerSecurityContext(); - UserSessionBase session = new UserSessionBase(); - session.setSSOEnabled(Boolean.TRUE); - ctx.setUserSession(session); - RangerContextHolder.setSecurityContext(ctx); - - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - - when(req.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); - when(req.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest"); - when(req.getRequestURI()).thenReturn("/index.html"); - when(req.getSession()).thenReturn(Mockito.mock(HttpSession.class)); - when(req.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); - when(req.getRequestURL()).thenReturn(new StringBuffer("http://localhost/index.html")); - - f.doFilter(req, res, chain); - - verify(res, times(1)).setHeader("X-Frame-Options", "DENY"); - verify(res, times(1)).setStatus(Mockito.anyInt()); - verify(res, times(1)).setHeader(Mockito.eq("X-Rngr-Redirect-Url"), Mockito.anyString()); - } - - @Test - public void testPrivateGetAuthoritiesAndGetGrantedAuthority_viaReflection() throws Exception { - RangerSSOAuthenticationFilter filter = new RangerSSOAuthenticationFilter(makeProps()); - // inject userMgr - UserMgr userMgr = mock(UserMgr.class); - when(userMgr.getRolesByLoginId("bob")).thenReturn(Arrays.asList("ROLE_USER", "ROLE_ADMIN")); - Field uf = RangerSSOAuthenticationFilter.class.getDeclaredField("userMgr"); - uf.setAccessible(true); - uf.set(filter, userMgr); - - Method mAuths = RangerSSOAuthenticationFilter.class.getDeclaredMethod("getAuthorities", String.class); - mAuths.setAccessible(true); - @SuppressWarnings("unchecked") - List roles = (List) mAuths.invoke(filter, "bob"); - assertNotNull(roles); - assertEquals(2, roles.size()); - - Method mGranted = RangerSSOAuthenticationFilter.class.getDeclaredMethod("getGrantedAuthority", Authentication.class); - mGranted.setAccessible(true); - Authentication input = new UsernamePasswordAuthenticationToken("bob", "", Collections.emptyList()); - Authentication out = (Authentication) mGranted.invoke(filter, input); - assertNotNull(out); - Object principal = out.getPrincipal(); - assertTrue(principal instanceof User); - assertEquals("bob", ((User) principal).getUsername()); - } - - @Test - public void testDoFilter_ssoEnabled_invalidToken_browserRedirects() throws Exception { - // Enable SSO via session - SecurityContextHolder.clearContext(); - RangerSecurityContext ctx = new RangerSecurityContext(); - UserSessionBase session = new UserSessionBase(); - session.setSSOEnabled(Boolean.TRUE); - ctx.setUserSession(session); - RangerContextHolder.setSecurityContext(ctx); - - // Filter with properties - SSOAuthenticationProperties p = makeProps(); - // Provide a public key but we'll mock token to fail validation chain - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(p) { - @Override - protected boolean validateToken(SignedJWT jwtToken) { - return false; // force invalid - } - }; - - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - - when(req.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); - when(req.getHeader("X-Requested-With")).thenReturn(null); - when(req.getRequestURI()).thenReturn("/index.html"); - when(req.getSession()).thenReturn(Mockito.mock(HttpSession.class)); - when(req.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); - when(req.getRequestURL()).thenReturn(new StringBuffer("http://localhost/index.html")); - // Provide JWT cookie so branch goes into validateToken, then invalid path - Cookie jwt = new Cookie(p.getCookieName(), "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhIn0.sign"); - when(req.getCookies()).thenReturn(new Cookie[] {jwt}); - - f.doFilter(req, res, chain); - - verify(res, times(1)).sendRedirect(Mockito.anyString()); - verify(chain, times(0)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); - } - - @Test - public void testDoFilter_ssoDisabled_locallogin_redirectsToLoginJsp() throws Exception { - SecurityContextHolder.clearContext(); - RangerContextHolder.setSecurityContext(new RangerSecurityContext()); - - RangerSSOAuthenticationFilter f = new RangerSSOAuthenticationFilter(makeProps()); - - HttpServletRequest req = Mockito.mock(HttpServletRequest.class); - HttpServletResponse res = Mockito.mock(HttpServletResponse.class); - FilterChain chain = Mockito.mock(FilterChain.class); - - when(req.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); - when(req.getRequestURI()).thenReturn("/locallogin"); - when(req.getSession()).thenReturn(Mockito.mock(HttpSession.class)); - when(req.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); - - f.doFilter(req, res, chain); - - verify(res, times(1)).sendRedirect("/login.jsp"); - verify(chain, times(0)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); - } -} diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSecurityContextFormationFilter.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSecurityContextFormationFilter.java index e468de58f47..94e594c5981 100644 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSecurityContextFormationFilter.java +++ b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerSecurityContextFormationFilter.java @@ -153,15 +153,15 @@ public void testGetAuthType_reflectionVariants() throws Exception { assertEquals(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY, m.invoke(filter, new RangerAuthenticationToken(userDetails, authorities, XXAuthSession.AUTH_TYPE_TRUSTED_PROXY), emptyRequest)); - // SSO — identified via request attribute (RangerSSOAuthenticationFilter sets UsernamePasswordAuthenticationToken) - HttpServletRequest reqSso = Mockito.mock(HttpServletRequest.class); - Mockito.when(reqSso.getAttribute("ssoEnabled")).thenReturn(true); + // JWT/Bearer — identified via the jwtAuthenticated request attribute, recorded as SSO for audit continuity + HttpServletRequest reqJwt = Mockito.mock(HttpServletRequest.class); + Mockito.when(reqJwt.getAttribute("jwtAuthenticated")).thenReturn(true); assertEquals(XXAuthSession.AUTH_TYPE_SSO, - m.invoke(filter, new org.springframework.security.authentication.UsernamePasswordAuthenticationToken("u", "pwd", authorities), reqSso)); + m.invoke(filter, new org.springframework.security.authentication.UsernamePasswordAuthenticationToken("u", "pwd", authorities), reqJwt)); // Kerberos — identified via spnegoEnabled attribute HttpServletRequest reqKrb = Mockito.mock(HttpServletRequest.class); - Mockito.when(reqKrb.getAttribute("ssoEnabled")).thenReturn(false); + Mockito.when(reqKrb.getAttribute("jwtAuthenticated")).thenReturn(false); Mockito.when(reqKrb.getAttribute("spnegoEnabled")).thenReturn(true); Mockito.when(reqKrb.getAttribute("trustedProxyEnabled")).thenReturn(false); assertEquals(XXAuthSession.AUTH_TYPE_KERBEROS, @@ -169,15 +169,14 @@ public void testGetAuthType_reflectionVariants() throws Exception { // Kerberos trusted proxy — both spnegoEnabled and trustedProxyEnabled HttpServletRequest reqKrbTp = Mockito.mock(HttpServletRequest.class); - Mockito.when(reqKrbTp.getAttribute("ssoEnabled")).thenReturn(false); + Mockito.when(reqKrbTp.getAttribute("jwtAuthenticated")).thenReturn(false); Mockito.when(reqKrbTp.getAttribute("spnegoEnabled")).thenReturn(true); Mockito.when(reqKrbTp.getAttribute("trustedProxyEnabled")).thenReturn(true); assertEquals(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY, m.invoke(filter, new org.springframework.security.authentication.UsernamePasswordAuthenticationToken("u", "pwd", authorities), reqKrbTp)); - // Password — no RangerAuthenticationToken, ssoEnabled explicitly false, no Kerberos attributes + // Password — no RangerAuthenticationToken, no JWT/Kerberos attributes HttpServletRequest reqPwd = Mockito.mock(HttpServletRequest.class); - Mockito.when(reqPwd.getAttribute("ssoEnabled")).thenReturn(false); assertEquals(XXAuthSession.AUTH_TYPE_PASSWORD, m.invoke(filter, new org.springframework.security.authentication.UsernamePasswordAuthenticationToken("u", "pwd", authorities), reqPwd)); } diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthentication.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthentication.java deleted file mode 100644 index ea90d85f5cf..00000000000 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthentication.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import com.nimbusds.jwt.SignedJWT; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** -* @generated by Cursor -* @description -*/ -@ExtendWith(MockitoExtension.class) -@TestMethodOrder(MethodOrderer.MethodName.class) -public class TestSSOAuthentication { - @Test - public void testTokenAndAuthenticatedFlag() throws Exception { - SignedJWT jwt = SignedJWT.parse("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhIn0.signature"); - SSOAuthentication sso = new SSOAuthentication(jwt); - - assertSame(jwt, sso.getCredentials()); - assertFalse(sso.isAuthenticated()); - assertNull(sso.getAuthorities()); - assertNull(sso.getDetails()); - assertNull(sso.getPrincipal()); - assertNull(sso.getName()); - - sso.setAuthenticated(true); - assertTrue(sso.isAuthenticated()); - } -} diff --git a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthenticationProperties.java b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthenticationProperties.java deleted file mode 100644 index a84340e4662..00000000000 --- a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestSSOAuthenticationProperties.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.ranger.security.web.filter; - -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** -* @generated by Cursor -* @description -*/ -@ExtendWith(MockitoExtension.class) -@TestMethodOrder(MethodOrderer.MethodName.class) -public class TestSSOAuthenticationProperties { - @Test - public void testGettersSetters() { - SSOAuthenticationProperties props = new SSOAuthenticationProperties(); - - props.setAuthenticationProviderUrl("https://knox/gateway/knoxsso/api/v1/websso"); - props.setCookieName("hadoop-jwt"); - props.setOriginalUrlQueryParam("originalUrl"); - props.setUserAgentList(new String[] {"Mozilla", "Chrome"}); - List audiences = Arrays.asList("ranger-admin", "ranger-users"); - props.setAudiences(audiences); - props.setExpectedSigAlg("RS256"); - - assertEquals("https://knox/gateway/knoxsso/api/v1/websso", props.getAuthenticationProviderUrl()); - assertEquals("hadoop-jwt", props.getCookieName()); - assertEquals("originalUrl", props.getOriginalUrlQueryParam()); - assertArrayEquals(new String[] {"Mozilla", "Chrome"}, props.getUserAgentList()); - assertEquals(audiences, props.getAudiences()); - assertEquals("RS256", props.getExpectedSigAlg()); - } -}