private void checkPasswordChangeIsAllowed(String userId, String oldPassword) { if (securityContextAccessor.isClient()) { // Trusted client (not acting on behalf of user) return; } // Call is by or on behalf of end user String currentUser = securityContextAccessor.getUserId(); if (securityContextAccessor.isAdmin()) { // even an admin needs to provide the old value to change his // password if (userId.equals(currentUser) && !StringUtils.hasText(oldPassword)) { throw new InvalidPasswordException("Previous password is required even for admin"); } } else { if (!userId.equals(currentUser)) { logger.warn( "User with id " + currentUser + " attempting to change password for user " + userId); // TODO: This should be audited when we have non-authentication // events in the log throw new InvalidPasswordException("Not permitted to change another user's password"); } // User is changing their own password, old password is required if (!StringUtils.hasText(oldPassword)) { throw new InvalidPasswordException("Previous password is required"); } } }
/** * Create an authorization request applying various UAA rules to the authorizationParameters and * the registered client details. * * <ul> * <li>For client_credentials grants, the default scopes are the client's granted authorities * <li>For other grant types the default scopes are the registered scopes in the client details * <li>Only scopes in those lists are valid, otherwise there is an exception * <li>If the scopes contain separators then resource ids are extracted as the scope value up to * the last index of the separator * <li>Some scopes can be hard-wired to resource ids (like the open id connect values), in which * case the separator is ignored * </ul> * * @see * org.springframework.security.oauth2.provider.AuthorizationRequestFactory#createAuthorizationRequest(java.util.Map, * java.lang.String, java.lang.String, java.util.Set) */ @Override public AuthorizationRequest createAuthorizationRequest( Map<String, String> authorizationParameters) { String clientId = authorizationParameters.get("client_id"); BaseClientDetails clientDetails = new BaseClientDetails(clientDetailsService.loadClientByClientId(clientId)); Set<String> scopes = OAuth2Utils.parseParameterList(authorizationParameters.get("scope")); String grantType = authorizationParameters.get("grant_type"); if ((scopes == null || scopes.isEmpty())) { if ("client_credentials".equals(grantType)) { // The client authorities should be a list of scopes scopes = AuthorityUtils.authorityListToSet(clientDetails.getAuthorities()); } else { // The default for a user token is the scopes registered with // the client scopes = clientDetails.getScope(); } } Set<String> scopesFromExternalAuthorities = null; if (!"client_credentials".equals(grantType) && securityContextAccessor.isUser()) { scopes = checkUserScopes(scopes, securityContextAccessor.getAuthorities(), clientDetails); // TODO: will the grantType ever contain client_credentials or // authorization_code // External Authorities are things like LDAP groups that will be // mapped to Oauth scopes // Add those scopes to the request. These scopes will not be // validated against the scopes // registered to a client. // These scopes also do not need approval. The fact that they are // already in an external // group communicates user approval. Denying approval does not mean // much scopesFromExternalAuthorities = findScopesFromAuthorities(authorizationParameters.get("authorities")); } Set<String> resourceIds = getResourceIds(clientDetails, scopes); clientDetails.setResourceIds(resourceIds); DefaultAuthorizationRequest request = new DefaultAuthorizationRequest(authorizationParameters); if (!scopes.isEmpty()) { request.setScope(scopes); } if (scopesFromExternalAuthorities != null) { Map<String, String> existingAuthorizationParameters = new LinkedHashMap<String, String>(); existingAuthorizationParameters.putAll(request.getAuthorizationParameters()); existingAuthorizationParameters.put( "external_scopes", OAuth2Utils.formatParameterList(scopesFromExternalAuthorities)); request.setAuthorizationParameters(existingAuthorizationParameters); } request.addClientDetails(clientDetails); return request; }
@Test public void clientCanChangeUserPasswordWithoutCurrentPassword() { SecurityContextAccessor sca = mockSecurityContext(joel); when(sca.isClient()).thenReturn(true); endpoints.setSecurityContextAccessor(sca); PasswordChangeRequest change = new PasswordChangeRequest(); change.setPassword("newpassword"); endpoints.changePassword(joel.getId(), change); }
@Test public void adminCanChangeAnotherUsersPassword() { SecurityContextAccessor sca = mockSecurityContext(dale); when(sca.isAdmin()).thenReturn(true); endpoints.setSecurityContextAccessor(sca); PasswordChangeRequest change = new PasswordChangeRequest(); change.setPassword("newpassword"); endpoints.changePassword(joel.getId(), change); }
@SuppressWarnings("unchecked") @Before public void init() { endpoints.setScimUserEndpoints(scimUserEndpoints); endpoints.setEnabled(true); when(securityContextAccessor.getAuthorities()).thenReturn(authorities); when(securityContextAccessor.getAuthenticationInfo()).thenReturn("mock object"); when(provisioning.retrieveActive(anyString())) .thenReturn( Collections.singletonList(MultitenancyFixture.identityProvider("test-origin", "uaa"))); endpoints.setSecurityContextAccessor(securityContextAccessor); }
private void checkPasswordChangeIsAllowed(ClientDetails clientDetails, String oldSecret) { if (!securityContextAccessor.isClient()) { // Trusted client (not acting on behalf of user) throw new IllegalStateException("Only a client can change client secret"); } String clientId = clientDetails.getClientId(); // Call is by client String currentClientId = securityContextAccessor.getClientId(); if (securityContextAccessor.isAdmin()) { // even an admin needs to provide the old value to change password if (clientId.equals(currentClientId) && !StringUtils.hasText(oldSecret)) { throw new IllegalStateException("Previous secret is required even for admin"); } } else { if (!clientId.equals(currentClientId)) { logger.warn( "Client with id " + currentClientId + " attempting to change password for client " + clientId); // TODO: This should be audited when we have non-authentication events in the log throw new IllegalStateException( "Bad request. Not permitted to change another client's secret"); } // Client is changing their own secret, old password is required if (!StringUtils.hasText(oldSecret)) { throw new IllegalStateException("Previous secret is required"); } } }
@RequestMapping(value = "/ids/Users") @ResponseBody public SearchResults<?> findUsers( @RequestParam(required = true, defaultValue = "") String filter, @RequestParam(required = false, defaultValue = "ascending") String sortOrder, @RequestParam(required = false, defaultValue = "1") int startIndex, @RequestParam(required = false, defaultValue = "100") int count, @RequestParam(required = false, defaultValue = "false") boolean includeInactive) { if (!enabled) { logger.warn( "Request from user " + securityContextAccessor.getAuthenticationInfo() + " received at disabled Id translation endpoint with filter:" + filter); throw new ScimException("Illegal operation.", HttpStatus.BAD_REQUEST); } filter = filter.trim(); checkFilter(filter); List<IdentityProvider> activeIdentityProviders = provisioning.retrieveActive(IdentityZoneHolder.get().getId()); if (!includeInactive) { if (activeIdentityProviders.isEmpty()) { return new SearchResults<>( Arrays.asList(ScimCore.SCHEMAS), new ArrayList<>(), startIndex, count, 0); } String originFilter = activeIdentityProviders .stream() .map( identityProvider -> "".concat("origin eq \"" + identityProvider.getOriginKey() + "\"")) .collect(Collectors.joining(" OR ")); filter += " AND (" + originFilter + " )"; } return scimUserEndpoints.findUsers( "id,userName,origin", filter, "userName", sortOrder, startIndex, count); }
private SecurityContextAccessor mockSecurityContextAccessor(String userName) { SecurityContextAccessor sca = mock(SecurityContextAccessor.class); when(sca.getUserName()).thenReturn(userName); when(sca.isUser()).thenReturn(true); return sca; }
private SecurityContextAccessor mockSecurityContext(ScimUser user) { SecurityContextAccessor sca = mock(SecurityContextAccessor.class); String id = user.getId(); when(sca.getUserId()).thenReturn(id); return sca; }