/*
*
* 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.saml;
import com.netflix.genie.test.categories.UnitTest;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Timer;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.saml.SAMLCredential;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* Tests for the SAMLUserDetailsImpl class. Makes sure we're pulling the right attributes and authorities from the
* SAML assertion.
*
* @author tgianos
* @since 3.0.0
*/
@Category(UnitTest.class)
public class SAMLUserDetailsServiceImplUnitTests {
private static final String USER_ATTRIBUTE_NAME = UUID.randomUUID().toString();
private static final String GROUP_ATTRIBUTE_NAME = UUID.randomUUID().toString();
private static final String ADMIN_GROUP = UUID.randomUUID().toString();
private static final String USER_ID = UUID.randomUUID().toString();
private static final String[] GROUPS = {UUID.randomUUID().toString(), ADMIN_GROUP, UUID.randomUUID().toString()};
private SAMLUserDetailsServiceImpl service;
private Timer loadAuthenticationTimer;
/**
* Setup the tests.
*/
@Before
public void setup() {
final SAMLProperties samlProperties = new SAMLProperties();
final SAMLProperties.Attributes attributes = new SAMLProperties.Attributes();
final SAMLProperties.Attributes.User user = new SAMLProperties.Attributes.User();
user.setName(USER_ATTRIBUTE_NAME);
attributes.setUser(user);
final SAMLProperties.Attributes.Groups groups = new SAMLProperties.Attributes.Groups();
groups.setName(GROUP_ATTRIBUTE_NAME);
groups.setAdmin(ADMIN_GROUP);
attributes.setGroups(groups);
samlProperties.setAttributes(attributes);
this.loadAuthenticationTimer = Mockito.mock(Timer.class);
final Registry registry = Mockito.mock(Registry.class);
Mockito.when(registry.timer(Mockito.anyString())).thenReturn(this.loadAuthenticationTimer);
this.service = new SAMLUserDetailsServiceImpl(samlProperties, registry);
}
/**
* Test to make sure a null credential throws exception.
*/
@Test(expected = UsernameNotFoundException.class)
public void doesThrowErrorOnNullCredential() {
this.service.loadUserBySAML(null);
Mockito
.verify(this.loadAuthenticationTimer, Mockito.times(1))
.record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS));
}
/**
* Make sure if no username is found an exception is thrown.
*/
@Test(expected = UsernameNotFoundException.class)
public void doesThrowErrorOnUserIdNotFound() {
final SAMLCredential credential = Mockito.mock(SAMLCredential.class);
Mockito.when(credential.getAttributeAsString(Mockito.eq(USER_ATTRIBUTE_NAME))).thenReturn(null);
this.service.loadUserBySAML(credential);
Mockito
.verify(this.loadAuthenticationTimer, Mockito.times(1))
.record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS));
}
/**
* Make sure if no groups are found but a user id is that the user logs in but only gets role user.
*/
@Test
public void canLoadUserWithoutGroups() {
final SAMLCredential credential = Mockito.mock(SAMLCredential.class);
Mockito.when(credential.getAttributeAsString(Mockito.eq(USER_ATTRIBUTE_NAME))).thenReturn(USER_ID);
Mockito.when(credential.getAttributeAsStringArray(Mockito.eq(GROUP_ATTRIBUTE_NAME))).thenReturn(null);
final Object result = this.service.loadUserBySAML(credential);
Assert.assertThat(result, Matchers.notNullValue());
Assert.assertTrue(result instanceof User);
final User user = (User) result;
Assert.assertThat(user.getUsername(), Matchers.is(USER_ID));
Assert.assertThat(user.getAuthorities(), Matchers.contains(new SimpleGrantedAuthority("ROLE_USER")));
Mockito
.verify(this.loadAuthenticationTimer, Mockito.times(1))
.record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS));
}
/**
* Make sure if user logs in and has admin group they get admin rights.
*/
@Test
public void canLoadUserWithAdminGroup() {
final SAMLCredential credential = Mockito.mock(SAMLCredential.class);
Mockito.when(credential.getAttributeAsString(Mockito.eq(USER_ATTRIBUTE_NAME))).thenReturn(USER_ID);
Mockito.when(credential.getAttributeAsStringArray(Mockito.eq(GROUP_ATTRIBUTE_NAME))).thenReturn(GROUPS);
final Object result = this.service.loadUserBySAML(credential);
Assert.assertThat(result, Matchers.notNullValue());
Assert.assertTrue(result instanceof User);
final User user = (User) result;
Assert.assertThat(user.getUsername(), Matchers.is(USER_ID));
Assert.assertThat(
user.getAuthorities(),
Matchers.hasItems(new SimpleGrantedAuthority("ROLE_USER"), new SimpleGrantedAuthority("ROLE_ADMIN"))
);
Mockito
.verify(this.loadAuthenticationTimer, Mockito.times(1))
.record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS));
}
/**
* Make sure if user logs in and doesn't have admin group user only gets user role.
*/
@Test
public void canLoadUserWithoutAdminGroup() {
final SAMLCredential credential = Mockito.mock(SAMLCredential.class);
Mockito.when(credential.getAttributeAsString(Mockito.eq(USER_ATTRIBUTE_NAME))).thenReturn(USER_ID);
Mockito.when(credential.getAttributeAsStringArray(Mockito.eq(GROUP_ATTRIBUTE_NAME)))
.thenReturn(new String[]{UUID.randomUUID().toString(), UUID.randomUUID().toString()});
final Object result = this.service.loadUserBySAML(credential);
Assert.assertThat(result, Matchers.notNullValue());
Assert.assertTrue(result instanceof User);
final User user = (User) result;
Assert.assertThat(user.getUsername(), Matchers.is(USER_ID));
Assert.assertThat(
user.getAuthorities(),
Matchers.contains(new SimpleGrantedAuthority("ROLE_USER"))
);
Assert.assertThat(
user.getAuthorities().size(),
Matchers.is(1)
);
Mockito
.verify(this.loadAuthenticationTimer, Mockito.times(1))
.record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS));
}
}