package com.sequenceiq.periscope.service.security; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import javax.annotation.PostConstruct; import javax.ws.rs.client.Client; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.crypto.codec.Base64; import org.springframework.stereotype.Service; import com.sequenceiq.cloudbreak.client.AccessToken; import com.sequenceiq.cloudbreak.client.IdentityClient; import com.sequenceiq.periscope.domain.PeriscopeUser; @Service public class RemoteUserDetailsService implements UserDetailsService { private static final int ACCOUNT_PART = 2; @Autowired private Client restClient; @Autowired private IdentityClient identityClient; @Autowired @Qualifier("identityServerUrl") private String identityServerUrl; @Value("${periscope.client.secret}") private String clientSecret; private WebTarget identityWebTarget; @PostConstruct public void init() { identityWebTarget = restClient.target(identityServerUrl).path("Users"); } @Override @Cacheable("userCache") @SuppressWarnings("unchecked") public PeriscopeUser getDetails(String filterValue, UserFilterField filterField) { WebTarget target; switch (filterField) { case USERNAME: target = identityWebTarget.queryParam("filter", "userName eq \"" + filterValue + "\""); break; case USER_ID: target = identityWebTarget.path(filterValue); break; default: throw new UserDetailsUnavailableException("User details cannot be retrieved."); } AccessToken accessToken = identityClient.getToken(clientSecret); String scimResponse = target.request(MediaType.APPLICATION_JSON).header("Authorization", "Bearer " + accessToken.getToken()).get(String.class); ObjectMapper mapper = new ObjectMapper(); try { JsonNode root = mapper.readTree(scimResponse); JsonNode userNode = root; if (UserFilterField.USERNAME.equals(filterField)) { userNode = root.get("resources").get(0); } String account = null; for (Iterator<JsonNode> iterator = userNode.get("groups").getElements(); iterator.hasNext();) { JsonNode node = iterator.next(); String group = node.get("display").asText(); if (group.startsWith("sequenceiq.account")) { String[] parts = group.split("\\."); if (account != null && !account.equals(parts[ACCOUNT_PART])) { throw new IllegalStateException("A user can belong to only one account."); } account = parts[ACCOUNT_PART]; } } String userId = userNode.get("id").asText(); String email = userNode.get("userName").asText(); return new PeriscopeUser(userId, email, account); } catch (IOException e) { throw new UserDetailsUnavailableException("User details cannot be retrieved from identity server.", e); } } private String getAuthorizationHeader(String clientId, String clientSecret) { String creds = String.format("%s:%s", clientId, clientSecret); try { return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8"))); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Could not convert String"); } } }