/* * * Copyright 2016 Netflix, Inc. * * Licensed 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 com.netflix.genie.web.security.x509; import com.google.common.collect.Sets; import com.netflix.genie.web.security.SecurityConditions; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Conditional; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; import java.util.Set; /** * Get user details from an X509 Certificate Token passed in. * * @author tgianos * @since 3.0.0 */ @Conditional(SecurityConditions.AnySecurityEnabled.class) @Component @Slf4j public class X509UserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> { private static final String ROLE_PREFIX = "ROLE_"; private static final GrantedAuthority USER_AUTHORITY = new SimpleGrantedAuthority(ROLE_PREFIX + "USER"); /** * {@inheritDoc} */ @Override public UserDetails loadUserDetails( final PreAuthenticatedAuthenticationToken token ) throws UsernameNotFoundException { log.debug("Entering loadUserDetails with token {}", token); // Assuming format of the principal is {username}:{role1,role2....} final Object principalObject = token.getPrincipal(); if (principalObject == null || !(principalObject instanceof String)) { throw new UsernameNotFoundException("Expected principal to be a String"); } final String principal = (String) principalObject; final String[] usernameAndRoles = principal.split(":"); if (usernameAndRoles.length != 2) { throw new UsernameNotFoundException("User and roles not found. Must be in format {user}:{role1,role2...}"); } final String username = usernameAndRoles[0]; final String[] roles = usernameAndRoles[1].split(","); if (roles.length == 0) { throw new UsernameNotFoundException("No roles found. Unable to authenticate"); } // If the certificate is valid the client is at the least a valid user final Set<GrantedAuthority> authorities = Sets.newHashSet(USER_AUTHORITY); for (final String role : roles) { authorities.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.toUpperCase())); } final User user = new User(username, "NA", authorities); log.info("User {} authenticated via client certificate", user); return user; } }