001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 *******************************************************************************/ 016package org.mitre.oauth2.service.impl; 017 018import java.io.UnsupportedEncodingException; 019import java.math.BigInteger; 020import java.security.SecureRandom; 021import java.util.Collection; 022import java.util.HashSet; 023 024import org.mitre.oauth2.model.ClientDetailsEntity; 025import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; 026import org.mitre.oauth2.service.ClientDetailsEntityService; 027import org.mitre.openid.connect.config.ConfigurationPropertiesBean; 028import org.springframework.beans.factory.annotation.Autowired; 029import org.springframework.security.core.GrantedAuthority; 030import org.springframework.security.core.authority.SimpleGrantedAuthority; 031import org.springframework.security.core.userdetails.User; 032import org.springframework.security.core.userdetails.UserDetails; 033import org.springframework.security.core.userdetails.UserDetailsService; 034import org.springframework.security.core.userdetails.UsernameNotFoundException; 035import org.springframework.security.oauth2.common.exceptions.InvalidClientException; 036import org.springframework.stereotype.Service; 037import org.springframework.web.util.UriUtils; 038 039import com.google.common.base.Strings; 040 041/** 042 * Loads client details based on URI encoding as passed in from basic auth. 043 * 044 * Should only get called if non-encoded provider fails. 045 * 046 * @author AANGANES 047 * 048 */ 049@Service("uriEncodedClientUserDetailsService") 050public class UriEncodedClientUserDetailsService implements UserDetailsService { 051 052 private static GrantedAuthority ROLE_CLIENT = new SimpleGrantedAuthority("ROLE_CLIENT"); 053 054 @Autowired 055 private ClientDetailsEntityService clientDetailsService; 056 057 @Autowired 058 private ConfigurationPropertiesBean config; 059 060 @Override 061 public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundException { 062 063 try { 064 String decodedClientId = UriUtils.decode(clientId, "UTF-8"); 065 066 ClientDetailsEntity client = clientDetailsService.loadClientByClientId(decodedClientId); 067 068 if (client != null) { 069 070 String encodedPassword = UriUtils.encodePathSegment(Strings.nullToEmpty(client.getClientSecret()), "UTF-8"); 071 072 if (config.isHeartMode() || // if we're running HEART mode turn off all client secrets 073 (client.getTokenEndpointAuthMethod() != null && 074 (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || 075 client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT)))) { 076 077 // Issue a random password each time to prevent password auth from being used (or skipped) 078 // for private key or shared key clients, see #715 079 080 encodedPassword = new BigInteger(512, new SecureRandom()).toString(16); 081 } 082 083 boolean enabled = true; 084 boolean accountNonExpired = true; 085 boolean credentialsNonExpired = true; 086 boolean accountNonLocked = true; 087 Collection<GrantedAuthority> authorities = new HashSet<>(client.getAuthorities()); 088 authorities.add(ROLE_CLIENT); 089 090 return new User(decodedClientId, encodedPassword, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); 091 } else { 092 throw new UsernameNotFoundException("Client not found: " + clientId); 093 } 094 } catch (UnsupportedEncodingException | InvalidClientException e) { 095 throw new UsernameNotFoundException("Client not found: " + clientId); 096 } 097 098 } 099 100 public ClientDetailsEntityService getClientDetailsService() { 101 return clientDetailsService; 102 } 103 104 public void setClientDetailsService(ClientDetailsEntityService clientDetailsService) { 105 this.clientDetailsService = clientDetailsService; 106 } 107 108}