diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrap.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrap.groovy index 4ca5b435b..2635f908c 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrap.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrap.groovy @@ -6,6 +6,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import groovy.util.logging.Slf4j import org.springframework.boot.context.event.ApplicationStartedEvent import org.springframework.context.event.EventListener @@ -19,11 +20,13 @@ class UserBootstrap { private final ShibUIConfiguration shibUIConfiguration private final UserRepository userRepository private final RoleRepository roleRepository + private final UserService userService - UserBootstrap(ShibUIConfiguration shibUIConfiguration, UserRepository userRepository, RoleRepository roleRepository) { + UserBootstrap(ShibUIConfiguration shibUIConfiguration, UserRepository userRepository, RoleRepository roleRepository, UserService userService) { this.shibUIConfiguration = shibUIConfiguration this.userRepository = userRepository this.roleRepository = roleRepository + this.userService = userService } @Transactional @@ -50,7 +53,7 @@ class UserBootstrap { it.emailAddress = email it } - userRepository.saveAndFlush(user) + userService.save(user) } } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index da33c53f8..c34d8e200 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -1,6 +1,27 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; +import javax.servlet.http.HttpServletRequest; + +import org.apache.lucene.analysis.Analyzer; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.core.io.Resource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.util.UrlPathHelper; + import com.fasterxml.jackson.databind.Module; + import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; @@ -13,7 +34,6 @@ import edu.internet2.tier.shibboleth.admin.ui.service.DefaultMetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService; import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl; -import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService; import edu.internet2.tier.shibboleth.admin.ui.service.EntityIdsSearchService; import edu.internet2.tier.shibboleth.admin.ui.service.EntityIdsSearchServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.EntityService; @@ -21,7 +41,6 @@ import edu.internet2.tier.shibboleth.admin.ui.service.FileWritingService; import edu.internet2.tier.shibboleth.admin.ui.service.FilterService; import edu.internet2.tier.shibboleth.admin.ui.service.FilterTargetService; -import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterTargetServiceImpl; @@ -29,33 +48,14 @@ import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.util.AttributeUtility; +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils; import edu.internet2.tier.shibboleth.admin.util.LuceneUtility; import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions; -import org.apache.lucene.analysis.Analyzer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.ResourceBundleMessageSource; -import org.springframework.core.io.Resource; -import org.springframework.web.servlet.LocaleResolver; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; -import org.springframework.web.util.UrlPathHelper; - -import javax.servlet.http.HttpServletRequest; @Configuration -@EnableConfigurationProperties({CustomPropertiesConfiguration.class, ShibUIConfiguration.class}) +@ComponentScan(basePackages="{ edu.internet2.tier.shibboleth.admin.ui.service }") +@EnableConfigurationProperties({ CustomPropertiesConfiguration.class, ShibUIConfiguration.class }) public class CoreShibUiConfiguration { - private static final Logger logger = LoggerFactory.getLogger(CoreShibUiConfiguration.class); - @Bean public OpenSamlObjects openSamlObjects() { return new OpenSamlObjects(); @@ -65,12 +65,7 @@ public OpenSamlObjects openSamlObjects() { public EntityService jpaEntityService() { return new JPAEntityServiceImpl(openSamlObjects()); } - - @Bean - public EntityDescriptorService jpaEntityDescriptorService(UserService userService) { - return new JPAEntityDescriptorServiceImpl(openSamlObjects(), jpaEntityService(), userService); - } - + @Bean public FilterService jpaFilterService() { return new JPAFilterServiceImpl(); @@ -99,13 +94,18 @@ public AttributeUtility attributeUtility() { @Bean @ConditionalOnProperty(name = "shibui.metadata-dir") - public EntityDescriptorFilesScheduledTasks entityDescriptorFilesScheduledTasks(EntityDescriptorRepository entityDescriptorRepository, @Value("${shibui.metadata-dir}") final String metadataDir) { - return new EntityDescriptorFilesScheduledTasks(metadataDir, entityDescriptorRepository, openSamlObjects(), fileWritingService()); + public EntityDescriptorFilesScheduledTasks entityDescriptorFilesScheduledTasks( + EntityDescriptorRepository entityDescriptorRepository, + @Value("${shibui.metadata-dir}") final String metadataDir) { + return new EntityDescriptorFilesScheduledTasks(metadataDir, entityDescriptorRepository, openSamlObjects(), + fileWritingService()); } @Bean @ConditionalOnProperty(name = "shibui.metadataProviders.target") - public MetadataProvidersScheduledTasks metadataProvidersScheduledTasks(@Value("${shibui.metadataProviders.target}") final Resource resource, final MetadataResolverService metadataResolverService) { + public MetadataProvidersScheduledTasks metadataProvidersScheduledTasks( + @Value("${shibui.metadataProviders.target}") final Resource resource, + final MetadataResolverService metadataResolverService) { return new MetadataProvidersScheduledTasks(resource, metadataResolverService, fileWritingService()); } @@ -124,7 +124,8 @@ public LocaleChangeInterceptor localeChangeInterceptor() { /** * A WebMvcConfigurer that won't mangle the path for the entities endpoint. * - * inspired by [ https://stackoverflow.com/questions/13482020/encoded-slash-2f-with-spring-requestmapping-path-param-gives-http-400 ] + * inspired by [ + * https://stackoverflow.com/questions/13482020/encoded-slash-2f-with-spring-requestmapping-path-param-gives-http-400 ] * * @return configurer */ @@ -166,10 +167,9 @@ public void addInterceptors(InterceptorRegistry registry) { } @Bean - public MetadataResolversPositionOrderContainerService - metadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository - positionOrderContainerRepository, - MetadataResolverRepository resolverRepository) { + public MetadataResolversPositionOrderContainerService metadataResolversPositionOrderContainerService( + MetadataResolversPositionOrderContainerRepository positionOrderContainerRepository, + MetadataResolverRepository resolverRepository) { return new DefaultMetadataResolversPositionOrderContainerService(positionOrderContainerRepository, resolverRepository); @@ -209,4 +209,11 @@ public UserService userService(RoleRepository roleRepository, UserRepository use public FileWritingService fileWritingService() { return new FileCheckingFileWritingService(); } + + @Bean + public EntityDescriptorConverstionUtils EntityDescriptorConverstionUtilsInit(EntityService entityService, OpenSamlObjects oso) { + EntityDescriptorConverstionUtils.setEntityService(entityService); + EntityDescriptorConverstionUtils.setOpenSamlObjects(oso); + return new EntityDescriptorConverstionUtils(); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java index f75f323be..3d66de957 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.User; import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; import edu.internet2.tier.shibboleth.admin.ui.security.springsecurity.AdminUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -28,10 +29,10 @@ import java.util.Collections; +import javax.transaction.Transactional; + /** * Web security configuration. - *

- * Workaround for slashes in URL from [https://stackoverflow.com/questions/48453980/spring-5-0-3-requestrejectedexception-the-request-was-rejected-because-the-url] */ @Configuration @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) @@ -46,6 +47,9 @@ public class WebSecurityConfig { @Autowired private UserRepository userRepository; + @Autowired + private UserService userService; + @Autowired private RoleRepository roleRepository; @@ -82,6 +86,7 @@ protected void configure(HttpSecurity http) throws Exception { } @Override + @Transactional protected void configure(AuthenticationManagerBuilder auth) throws Exception { // TODO: more configurable authentication PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); @@ -100,10 +105,10 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { }); u.setRoles(Collections.singleton(adminRole)); u.setEmailAddress("admin@localhost"); - return userRepository.saveAndFlush(u); + return userService.save(u); }); adminUser.setPassword(defaultPassword); - userRepository.saveAndFlush(adminUser); + userService.save(adminUser); auth .inMemoryAuthentication() diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java index d49a5a5f4..dacc88bfc 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java @@ -145,7 +145,7 @@ public IDPSSODescriptor getIDPSSODescriptor(String s) { } public Group getGroup() { - return group == null ? Group.DEFAULT_GROUP : group; + return group == null ? Group.ADMIN_GROUP : group; } @Transient diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java index 1c757b514..6ca5d7a4d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java @@ -110,7 +110,7 @@ public String getId() { } public String getGroupId() { - return groupId == null ? Group.DEFAULT_GROUP.getResourceId() : groupId; + return groupId == null ? Group.ADMIN_GROUP.getResourceId() : groupId; } public List getLogoutEndpoints() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java index b37b0dcf9..41edbe2fe 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java @@ -32,5 +32,6 @@ public interface EntityDescriptorRepository extends JpaRepository findAllByGroupIsNull(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java index 62a61ad49..1874e6d36 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java @@ -23,9 +23,18 @@ @Entity(name = "user_groups") @Data public class Group { + public Group() { + } + + public Group(User user) { + resourceId=user.getUsername(); + name=user.getUsername(); + description="default user-group"; + } + @Transient @JsonIgnore - public static Group DEFAULT_GROUP; + public static Group ADMIN_GROUP; @Column(name = "group_description", nullable = true) String description; @@ -46,7 +55,4 @@ public class Group { @JsonIgnore @EqualsAndHashCode.Exclude Set entityDescriptors; - - @Column(name = "default_group", nullable = false) - boolean defaultGroup = false; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java index 1bf4e963f..978c910e8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java @@ -70,12 +70,12 @@ public class User extends AbstractAuditable { private String username; public Group getGroup() { - return group == null ? Group.DEFAULT_GROUP : group; + return group; } public String getGroupId() { - if (groupId == null && getGroup() != null) { - groupId = getGroup().getResourceId(); + if (groupId == null) { + groupId = group == null ? null : getGroup().getResourceId(); } return groupId; } @@ -91,10 +91,11 @@ public String getRole() { return this.role; } + /** + * If (for some reason) the incoming group is null, the user is defaulted to their own group + */ public void setGroup(Group assignedGroup) { this.group = assignedGroup; - if (group != null) { - groupId = group.getResourceId(); - } + this.groupId = getGroup().getResourceId(); } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepository.java index 89df994a4..9576184e4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepository.java @@ -13,6 +13,4 @@ public interface GroupsRepository extends JpaRepository { @SuppressWarnings("unchecked") Group save(Group group); - - Group findByDefaultGroupTrue(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java index 2fcfb7587..040db27aa 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java @@ -1,11 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.security.service; import java.util.List; -import java.util.UUID; -import javax.annotation.PostConstruct; import javax.transaction.Transactional; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -16,10 +15,17 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository; @Service -public class GroupServiceImpl implements IGroupService { +public class GroupServiceImpl implements IGroupService, InitializingBean { @Autowired private GroupsRepository repo; + public GroupServiceImpl() { + } + + public GroupServiceImpl(GroupsRepository repo) { + this.repo = repo; + } + @Override public Group createGroup(Group group) throws GroupExistsConflictException { Group foundGroup = find(group.getResourceId()); @@ -63,18 +69,19 @@ public Group updateGroup(Group group) throws EntityNotFoundException { return repo.save(group); } - @PostConstruct + /** + * Ensure (mostly for migrations) that we have defined a default admin group + */ + @Override @Transactional - private void ensureDefaultGroupExists() { - // This is something of a migration piece to ensure there is a default group that users and entity descriptors - // can be assigned to out of the box. - Group g = repo.findByDefaultGroupTrue(); + public void afterPropertiesSet() { + Group g = repo.findByResourceId("admingroup"); if (g == null) { g = new Group(); - g.setDefaultGroup(true); - g.setName("DEFAULT-GROUP"); + g.setName("ADMIN-GROUP"); + g.setResourceId("admingroup"); g = repo.save(g); } - Group.DEFAULT_GROUP = g; + Group.ADMIN_GROUP = g; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 74849f5a5..25ca49f46 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -1,26 +1,55 @@ package edu.internet2.tier.shibboleth.admin.ui.security.service; +import edu.internet2.tier.shibboleth.admin.ui.security.exception.GroupExistsConflictException; import edu.internet2.tier.shibboleth.admin.ui.security.model.Group; import edu.internet2.tier.shibboleth.admin.ui.security.model.Role; import edu.internet2.tier.shibboleth.admin.ui.security.model.User; import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Optional; import java.util.Set; -public class UserService { +import javax.annotation.PostConstruct; +import javax.transaction.Transactional; + +@Service +public class UserService implements InitializingBean { + @Autowired + private IGroupService groupService; + + @Autowired private RoleRepository roleRepository; + + @Autowired private UserRepository userRepository; + /** + * Primarily for testing purposes so we can control the injections + */ public UserService(RoleRepository roleRepository, UserRepository userRepository) { this.roleRepository = roleRepository; this.userRepository = userRepository; } + + public boolean currentUserIsAdmin() { + User user = getCurrentUser(); + return user != null && user.getRole().equals("ROLE_ADMIN"); + } + @Override + @Transactional + public void afterPropertiesSet() { + // TODO: Ensure all the db users have a group - migration task + } + public User getCurrentUser() { //TODO: Consider returning an Optional here User user = null; @@ -35,6 +64,15 @@ public User getCurrentUser() { } return user; } + + public Group getCurrentUserGroup() { + switch (getCurrentUserAccess()) { + case ADMIN: + return Group.ADMIN_GROUP; + default: + return getCurrentUser().getGroup(); + } + } public UserAccess getCurrentUserAccess() { User user = getCurrentUser(); @@ -44,18 +82,17 @@ public UserAccess getCurrentUserAccess() { if (user.getRole().equals("ROLE_ADMIN")) { return UserAccess.ADMIN; } - if (user.getGroup() != null) { + if (user.getRole().equals("ROLE_USER")) { return UserAccess.GROUP; } return UserAccess.NONE; } - + public boolean isAuthorizedFor(Group objectGroup) { - String groupId = objectGroup == null ? "" : objectGroup.getResourceId(); - return isAuthorizedFor(groupId); + String objectGroupId = objectGroup == null ? Group.ADMIN_GROUP.getResourceId() : objectGroup.getResourceId(); + return isAuthorizedFor(objectGroupId); } - public boolean isAuthorizedFor(String objectGroupResourceId) { switch (getCurrentUserAccess()) { // no user returns NONE case ADMIN: @@ -69,6 +106,26 @@ public boolean isAuthorizedFor(String objectGroupResourceId) { return false; } } + + /** + * Creating users should always have a group. If the user isn't assigned to a group, create one + */ + public User save(User user) { + if (user.getRole().equalsIgnoreCase("ROLE_ADMIN")) { + user.setGroup(Group.ADMIN_GROUP); + } + else if (user.getGroupId() == null) { + Group g = new Group(user); + try { + g = groupService.createGroup(g); + } + catch (GroupExistsConflictException e) { + g = groupService.find(user.getUsername()); + } + user.setGroup(g); + } + return userRepository.save(user); + } /** * Given a user with a defined User.role, update the User.roles collection with that role. @@ -92,9 +149,4 @@ public void updateUserRole(User user) { throw new RuntimeException(String.format("User with username [%s] has no role defined and therefor cannot be updated!", user.getUsername())); } } - - public boolean currentUserIsAdmin() { - User user = getCurrentUser(); - return user != null && user.getRole().equals("ROLE_ADMIN"); - } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index 1ebdd7a6d..236622255 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -1,6 +1,5 @@ package edu.internet2.tier.shibboleth.admin.ui.service; - import com.google.common.base.Strings; import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse; @@ -9,7 +8,6 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBuilder; import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeValue; import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson; -import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPersonBuilder; import edu.internet2.tier.shibboleth.admin.ui.domain.Description; import edu.internet2.tier.shibboleth.admin.ui.domain.DisplayName; import edu.internet2.tier.shibboleth.admin.ui.domain.EmailAddress; @@ -62,9 +60,11 @@ import org.opensaml.xmlsec.signature.KeyInfo; import org.opensaml.xmlsec.signature.X509Certificate; import org.opensaml.xmlsec.signature.X509Data; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; @@ -79,34 +79,40 @@ import javax.annotation.PostConstruct; import javax.transaction.Transactional; -import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getStringListOfAttributeValues; - +import static edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils.*; +import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.*; -/** - * Default implementation of {@link EntityDescriptorService} - * - * @since 1.0 - */ @Slf4j -public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService { +@Service +public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService, InitializingBean { @Autowired - private EntityDescriptorRepository entityDescriptorRepository; + EntityDescriptorRepository entityDescriptorRepository; @Autowired - private EntityService entityService; - - @Autowired - private IGroupService groupService; + IGroupService groupService; @Autowired private OpenSamlObjects openSamlObjects; - private UserService userService; - - public JPAEntityDescriptorServiceImpl(OpenSamlObjects openSamlObjects, EntityService entityService, UserService userService) { - this.openSamlObjects = openSamlObjects; - this.entityService = entityService; - this.userService = userService; + @Autowired + UserService userService; + + @Override + @Transactional + public void afterPropertiesSet() { + // SHIBUI-1740: Adding admin group to all existing entity descriptors that do not have a group already. + // Because this class has a GroupService, we assume the ADMIN_GROUP has already been setup prior to being + // autowired into this class. + try { + entityDescriptorRepository.findAllByGroupIsNull().forEach(ed -> { + ed.setGroup(Group.ADMIN_GROUP); + entityDescriptorRepository.save(ed); + }); + } + catch (NullPointerException e) { + // This block was added due to a number of mock test where NPEs happened. Rather than wire more mock junk + // into tests that are only trying to compensate for this migration, this is here + } } private EntityDescriptor buildDescriptorFromRepresentation(final EntityDescriptor ed, final EntityDescriptorRepresentation representation) { @@ -137,35 +143,11 @@ public EntityDescriptor createDescriptorFromRepresentation(final EntityDescripto return buildDescriptorFromRepresentation(ed, representation); } - KeyDescriptor createKeyDescriptor(String name, String type, String value) { - KeyDescriptor keyDescriptor = openSamlObjects.buildDefaultInstanceOfType(KeyDescriptor.class); - - if (!Strings.isNullOrEmpty(name)) { - keyDescriptor.setName(name); - } - - if (!"both".equals(type)) { - keyDescriptor.setUsageType(type); - } - - KeyInfo keyInfo = openSamlObjects.buildDefaultInstanceOfType(KeyInfo.class); - keyDescriptor.setKeyInfo(keyInfo); - - X509Data x509Data = openSamlObjects.buildDefaultInstanceOfType(X509Data.class); - keyInfo.getXMLObjects().add(x509Data); - - X509Certificate x509Certificate = openSamlObjects.buildDefaultInstanceOfType(X509Certificate.class); - x509Data.getXMLObjects().add(x509Certificate); - x509Certificate.setValue(value); - - return keyDescriptor; - } - @Override public EntityDescriptorRepresentation createNew(EntityDescriptor ed) throws ForbiddenException, EntityIdExistsException { return createNew(createRepresentationFromDescriptor(ed)); } - + @Override public EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRep) throws ForbiddenException, EntityIdExistsException { if (edRep.isServiceEnabled() && !userService.currentUserIsAdmin()) { @@ -177,10 +159,10 @@ public EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation e } EntityDescriptor ed = (EntityDescriptor) createDescriptorFromRepresentation(edRep); - ed.setGroup(userService.getCurrentUser().getGroup()); + ed.setGroup(userService.getCurrentUserGroup()); return createRepresentationFromDescriptor(entityDescriptorRepository.save(ed)); } - + @Override public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.opensaml.saml.saml2.metadata.EntityDescriptor entityDescriptor) { EntityDescriptor ed = (EntityDescriptor) entityDescriptor; @@ -195,7 +177,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope representation.setVersion(ed.hashCode()); representation.setCreatedBy(ed.getCreatedBy()); representation.setCurrent(ed.isCurrent()); - representation.setGroupId(ed.getGroup() != null ? ed.getGroup().getResourceId() : Group.DEFAULT_GROUP.getResourceId()); + representation.setGroupId(ed.getGroup().getResourceId()); if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getSupportedProtocols().size() > 0) { ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptorRepresentation = representation.getServiceProviderSsoDescriptor(true); @@ -353,7 +335,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope if (jpaAttribute.getAttributeValues().size() != 1) { throw new RuntimeException("Multiple/No values detected where one is expected!"); } - attributeValues = getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0)); + attributeValues = ModelRepresentationConversions.getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0)); break; case INTEGER: if (jpaAttribute.getAttributeValues().size() != 1) { @@ -367,7 +349,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope } if (overrideProperty.getPersistType() != null && !overrideProperty.getPersistType().equals(overrideProperty.getDisplayType())) { - attributeValues = overrideProperty.getPersistValue().equals(getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0))); + attributeValues = overrideProperty.getPersistValue().equals(ModelRepresentationConversions.getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0))); } else { attributeValues = Boolean.valueOf(overrideProperty.getInvert()) ^ Boolean.valueOf(((XSBoolean) jpaAttribute.getAttributeValues() .get(0)).getStoredValue()); @@ -377,7 +359,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope case LIST: case SELECTION_LIST: attributeValues = jpaAttribute.getAttributeValues().stream() - .map(attributeValue -> getValueFromXMLObject(attributeValue)) + .map(attributeValue -> ModelRepresentationConversions.getValueFromXMLObject(attributeValue)) .collect(Collectors.toList()); } relyingPartyOverrides.put(((IRelyingPartyOverrideProperty) override.get()).getName(), attributeValues); @@ -431,32 +413,6 @@ public List getAttributeReleaseListFromAttributeList(List att return ModelRepresentationConversions.getAttributeReleaseListFromAttributeList(attributeList); } - private EntityAttributes getEntityAttributes(EntityDescriptor ed) { - return getEntityAttributes(ed, true); - } - - private EntityAttributes getEntityAttributes(EntityDescriptor ed, boolean create) { - Extensions extensions = ed.getExtensions(); - if (extensions == null && !create) { - return null; - } - if (extensions == null) { - extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); - ed.setExtensions(extensions); - } - - EntityAttributes entityAttributes = null; - if (extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).size() > 0) { - entityAttributes = (EntityAttributes) extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).get(0); - } else { - if (create) { - entityAttributes = ((EntityAttributesBuilder) openSamlObjects.getBuilderFactory().getBuilder(EntityAttributes.DEFAULT_ELEMENT_NAME)).buildObject(); - extensions.getUnknownXMLObjects().add(entityAttributes); - } - } - return entityAttributes; - } - @Override public EntityDescriptor getEntityDescriptorByResourceId(String resourceId) throws EntityNotFoundException, ForbiddenException { EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); @@ -468,306 +424,12 @@ public EntityDescriptor getEntityDescriptorByResourceId(String resourceId) throw } return ed; } - - private Optional getOptionalEntityAttributes(EntityDescriptor ed) { - return Optional.ofNullable(getEntityAttributes(ed, false)); - } - + @Override public Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { return ModelRepresentationConversions.getRelyingPartyOverridesRepresentationFromAttributeList(attributeList); } - - private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor) { - return getSPSSODescriptorFromEntityDescriptor(entityDescriptor, true); - } - - private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor, boolean create) { - if (entityDescriptor.getSPSSODescriptor("") == null && create) { - SPSSODescriptor spssoDescriptor = openSamlObjects.buildDefaultInstanceOfType(SPSSODescriptor.class); - entityDescriptor.getRoleDescriptors().add(spssoDescriptor); - } - return entityDescriptor.getSPSSODescriptor(""); - } - - private UIInfo getUIInfo(EntityDescriptor ed) { - Extensions extensions = getSPSSODescriptorFromEntityDescriptor(ed).getExtensions(); - if (extensions == null) { - extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); - ed.getSPSSODescriptor("").setExtensions(extensions); - } - - UIInfo uiInfo; - if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { - uiInfo = (UIInfo) extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0); - } else { - uiInfo = openSamlObjects.buildDefaultInstanceOfType(UIInfo.class); - extensions.getUnknownXMLObjects().add(uiInfo); - } - return uiInfo; - } - - @PostConstruct - @Transactional - private void migration() { - // SHIBUI-1740: Adding default group to all existing entity descriptors that do not have a group already. - // Because this class has a GroupService, we assume the DEFAULT_GROUP has already been setup prior to being - // autowired into this class. - try { - entityDescriptorRepository.findAllByGroupIsNull().forEach(ed -> { - ed.setGroup(Group.DEFAULT_GROUP); - entityDescriptorRepository.save(ed); - }); - } - catch (NullPointerException e) { - // This block was added due to a number of mock test where NPEs happened. Rather than wire more mock junk - // into tests that are only trying to compensate for this migration, this is here - } - } - - // TODO: remove - private String getValueFromXMLObject(XMLObject xmlObject) { - return ModelRepresentationConversions.getValueFromXMLObject(xmlObject); - } - - private void removeUIInfo(EntityDescriptor ed) { - SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed, false); - if (spssoDescriptor != null) { - Extensions extensions = spssoDescriptor.getExtensions(); - if (extensions == null) { - return; - } - if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { - extensions.getUnknownXMLObjects().remove(extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0)); - } - } - } - - void setupACSs(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup ACSs - if (representation.getAssertionConsumerServices() != null && representation.getAssertionConsumerServices().size() > 0) { - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); - for (AssertionConsumerServiceRepresentation acsRepresentation : representation.getAssertionConsumerServices()) { - AssertionConsumerService assertionConsumerService = openSamlObjects.buildDefaultInstanceOfType(AssertionConsumerService.class); - getSPSSODescriptorFromEntityDescriptor(ed).getAssertionConsumerServices().add(assertionConsumerService); - if (acsRepresentation.isMakeDefault()) { - assertionConsumerService.setIsDefault(true); - } - assertionConsumerService.setBinding(acsRepresentation.getBinding()); - assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); - assertionConsumerService.setIndex(acsRepresentation.getIndex()); - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); - } - } - - void setupContacts(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up contacts - if (representation.getContacts() != null && representation.getContacts().size() > 0) { - ed.getContactPersons().clear(); - for (ContactRepresentation contactRepresentation : representation.getContacts()) { - ContactPerson contactPerson = ((ContactPersonBuilder) openSamlObjects.getBuilderFactory().getBuilder(ContactPerson.DEFAULT_ELEMENT_NAME)).buildObject(); - - contactPerson.setType(contactRepresentation.getType()); - - GivenName givenName = openSamlObjects.buildDefaultInstanceOfType(GivenName.class); - givenName.setName(contactRepresentation.getName()); - contactPerson.setGivenName(givenName); - - EmailAddress emailAddress = openSamlObjects.buildDefaultInstanceOfType(EmailAddress.class); - emailAddress.setAddress(contactRepresentation.getEmailAddress()); - contactPerson.addEmailAddress(emailAddress); - - ed.addContactPerson(contactPerson); - } - } else { - ed.getContactPersons().clear(); - } - } - - void setupLogout(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup logout - if (representation.getLogoutEndpoints() != null && !representation.getLogoutEndpoints().isEmpty()) { - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); - for (LogoutEndpointRepresentation logoutEndpointRepresentation : representation.getLogoutEndpoints()) { - SingleLogoutService singleLogoutService = openSamlObjects.buildDefaultInstanceOfType(SingleLogoutService.class); - singleLogoutService.setBinding(logoutEndpointRepresentation.getBindingType()); - singleLogoutService.setLocation(logoutEndpointRepresentation.getUrl()); - - getSPSSODescriptorFromEntityDescriptor(ed).getSingleLogoutServices().add(singleLogoutService); - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); - } - } - - void setupOrganization(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up organization - if (representation.getOrganization() != null && representation.getOrganization().getName() != null && representation.getOrganization().getDisplayName() != null && representation.getOrganization().getUrl() != null) { - OrganizationRepresentation organizationRepresentation = representation.getOrganization(); - Organization organization = openSamlObjects.buildDefaultInstanceOfType(Organization.class); - - OrganizationName organizationName = openSamlObjects.buildDefaultInstanceOfType(OrganizationName.class); - organizationName.setXMLLang("en"); - organizationName.setValue(organizationRepresentation.getName()); - organization.getOrganizationNames().add(organizationName); - - OrganizationDisplayName organizationDisplayName = openSamlObjects.buildDefaultInstanceOfType(OrganizationDisplayName.class); - organizationDisplayName.setXMLLang("en"); - organizationDisplayName.setValue(organizationRepresentation.getDisplayName()); - organization.getDisplayNames().add(organizationDisplayName); - - OrganizationURL organizationURL = openSamlObjects.buildDefaultInstanceOfType(OrganizationURL.class); - organizationURL.setXMLLang("en"); - organizationURL.setValue(organizationRepresentation.getUrl()); - organization.getURLs().add(organizationURL); - - ed.setOrganization(organization); - } else { - ed.setOrganization(null); - } - } - - void setupRelyingPartyOverrides(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - if (representation.getRelyingPartyOverrides() != null || (representation.getAttributeRelease() != null && representation.getAttributeRelease().size() > 0)) { - // TODO: review if we need more than a naive implementation - getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); - getEntityAttributes(ed).getAttributes().addAll(entityService.getAttributeListFromEntityRepresentation(representation)); - } else { - getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); - } - } - - void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup security - if (representation.getSecurityInfo() != null) { - SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); - if (securityInfoRepresentation.isAuthenticationRequestsSigned()) { - getSPSSODescriptorFromEntityDescriptor(ed).setAuthnRequestsSigned(true); - } - if (securityInfoRepresentation.isWantAssertionsSigned()) { - getSPSSODescriptorFromEntityDescriptor(ed).setWantAssertionsSigned(true); - } - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear()); - if (securityInfoRepresentation.isX509CertificateAvailable()) { - for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { - KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); - getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); - } - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent( spssoDescriptor -> { - spssoDescriptor.setAuthnRequestsSigned((Boolean) null); - spssoDescriptor.setWantAssertionsSigned((Boolean) null); - spssoDescriptor.getKeyDescriptors().clear(); - }); - } - } - - void setupSPSSODescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup SPSSODescriptor - if (representation.getServiceProviderSsoDescriptor() != null) { - SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed); - - spssoDescriptor.setSupportedProtocols(Collections.EMPTY_LIST); - if (!Strings.isNullOrEmpty(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum())) { - spssoDescriptor.setSupportedProtocols( - Arrays.stream(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum().split(",")).map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p.trim())).collect(Collectors.toList()) - ); - } - - spssoDescriptor.getNameIDFormats().clear(); - if (representation.getServiceProviderSsoDescriptor() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats().size() > 0) { - for (String nameidFormat : representation.getServiceProviderSsoDescriptor().getNameIdFormats()) { - NameIDFormat nameIDFormat = openSamlObjects.buildDefaultInstanceOfType(NameIDFormat.class); - - nameIDFormat.setFormat(nameidFormat); - - spssoDescriptor.getNameIDFormats().add(nameIDFormat); - } - } - } else { - ed.setRoleDescriptors(null); - } - } - - void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up mdui - if (representation.getMdui() != null) { - // TODO: check if we need more than a naive implementation - removeUIInfo(ed); - MduiRepresentation mduiRepresentation = representation.getMdui(); - - if (!Strings.isNullOrEmpty(mduiRepresentation.getDisplayName())) { - DisplayName displayName = openSamlObjects.buildDefaultInstanceOfType(DisplayName.class); - getUIInfo(ed).addDisplayName(displayName); - displayName.setValue(mduiRepresentation.getDisplayName()); - displayName.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getDisplayNames())); - } - - if (!Strings.isNullOrEmpty(mduiRepresentation.getInformationUrl())) { - InformationURL informationURL = openSamlObjects.buildDefaultInstanceOfType(InformationURL.class); - getUIInfo(ed).addInformationURL(informationURL); - informationURL.setValue(mduiRepresentation.getInformationUrl()); - informationURL.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getInformationURLs())); - } - - if (!Strings.isNullOrEmpty(mduiRepresentation.getPrivacyStatementUrl())) { - PrivacyStatementURL privacyStatementURL = openSamlObjects.buildDefaultInstanceOfType(PrivacyStatementURL.class); - getUIInfo(ed).addPrivacyStatementURL(privacyStatementURL); - privacyStatementURL.setValue(mduiRepresentation.getPrivacyStatementUrl()); - privacyStatementURL.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getPrivacyStatementURLs())); - } - - if (!Strings.isNullOrEmpty(mduiRepresentation.getDescription())) { - Description description = openSamlObjects.buildDefaultInstanceOfType(Description.class); - getUIInfo(ed).addDescription(description); - description.setValue(mduiRepresentation.getDescription()); - description.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getDescriptions())); - } - - if (!Strings.isNullOrEmpty(mduiRepresentation.getLogoUrl())) { - Logo logo = openSamlObjects.buildDefaultInstanceOfType(Logo.class); - getUIInfo(ed).addLogo(logo); - logo.setURL(mduiRepresentation.getLogoUrl()); - logo.setHeight(mduiRepresentation.getLogoHeight()); - logo.setWidth(mduiRepresentation.getLogoWidth()); - logo.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getLogos())); - } - } else { - removeUIInfo(ed); - } - } - + @Override public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRep) throws ForbiddenException, EntityNotFoundException { EntityDescriptor existingEd = entityDescriptorRepository.findByResourceId(edRep.getId()); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConverstionUtils.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConverstionUtils.java new file mode 100644 index 000000000..843567496 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConverstionUtils.java @@ -0,0 +1,378 @@ +package edu.internet2.tier.shibboleth.admin.util; + +import static edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils.getEntityAttributes; +import static edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils.getOptionalEntityAttributes; +import static edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils.getSPSSODescriptorFromEntityDescriptor; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.opensaml.xmlsec.signature.KeyInfo; +import org.opensaml.xmlsec.signature.X509Certificate; +import org.opensaml.xmlsec.signature.X509Data; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.common.base.Strings; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AssertionConsumerService; +import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson; +import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPersonBuilder; +import edu.internet2.tier.shibboleth.admin.ui.domain.Description; +import edu.internet2.tier.shibboleth.admin.ui.domain.DisplayName; +import edu.internet2.tier.shibboleth.admin.ui.domain.EmailAddress; +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributes; +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesBuilder; +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor; +import edu.internet2.tier.shibboleth.admin.ui.domain.Extensions; +import edu.internet2.tier.shibboleth.admin.ui.domain.GivenName; +import edu.internet2.tier.shibboleth.admin.ui.domain.InformationURL; +import edu.internet2.tier.shibboleth.admin.ui.domain.KeyDescriptor; +import edu.internet2.tier.shibboleth.admin.ui.domain.Logo; +import edu.internet2.tier.shibboleth.admin.ui.domain.NameIDFormat; +import edu.internet2.tier.shibboleth.admin.ui.domain.Organization; +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationDisplayName; +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationName; +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationURL; +import edu.internet2.tier.shibboleth.admin.ui.domain.PrivacyStatementURL; +import edu.internet2.tier.shibboleth.admin.ui.domain.SPSSODescriptor; +import edu.internet2.tier.shibboleth.admin.ui.domain.SingleLogoutService; +import edu.internet2.tier.shibboleth.admin.ui.domain.UIInfo; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.AssertionConsumerServiceRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ContactRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.LogoutEndpointRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityService; +import lombok.Setter; + +@Service +public class EntityDescriptorConverstionUtils { + @Autowired + @Setter + private static OpenSamlObjects openSamlObjects; + + @Autowired + @Setter + private static EntityService entityService; + + public static KeyDescriptor createKeyDescriptor(String name, String type, String value) { + KeyDescriptor keyDescriptor = openSamlObjects.buildDefaultInstanceOfType(KeyDescriptor.class); + + if (!Strings.isNullOrEmpty(name)) { + keyDescriptor.setName(name); + } + + if (!"both".equals(type)) { + keyDescriptor.setUsageType(type); + } + + KeyInfo keyInfo = openSamlObjects.buildDefaultInstanceOfType(KeyInfo.class); + keyDescriptor.setKeyInfo(keyInfo); + + X509Data x509Data = openSamlObjects.buildDefaultInstanceOfType(X509Data.class); + keyInfo.getXMLObjects().add(x509Data); + + X509Certificate x509Certificate = openSamlObjects.buildDefaultInstanceOfType(X509Certificate.class); + x509Data.getXMLObjects().add(x509Certificate); + x509Certificate.setValue(value); + + return keyDescriptor; + } + + public static EntityAttributes getEntityAttributes(EntityDescriptor ed) { + return getEntityAttributes(ed, true); + } + + public static EntityAttributes getEntityAttributes(EntityDescriptor ed, boolean create) { + Extensions extensions = ed.getExtensions(); + if (extensions == null && !create) { + return null; + } + if (extensions == null) { + extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); + ed.setExtensions(extensions); + } + + EntityAttributes entityAttributes = null; + if (extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).size() > 0) { + entityAttributes = (EntityAttributes) extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).get(0); + } else { + if (create) { + entityAttributes = ((EntityAttributesBuilder) openSamlObjects.getBuilderFactory().getBuilder(EntityAttributes.DEFAULT_ELEMENT_NAME)).buildObject(); + extensions.getUnknownXMLObjects().add(entityAttributes); + } + } + return entityAttributes; + } + + public static Optional getOptionalEntityAttributes(EntityDescriptor ed) { + return Optional.ofNullable(getEntityAttributes(ed, false)); + } + + public static SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor) { + return getSPSSODescriptorFromEntityDescriptor(entityDescriptor, true); + } + + public static SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor, boolean create) { + if (entityDescriptor.getSPSSODescriptor("") == null && create) { + SPSSODescriptor spssoDescriptor = openSamlObjects.buildDefaultInstanceOfType(SPSSODescriptor.class); + entityDescriptor.getRoleDescriptors().add(spssoDescriptor); + } + return entityDescriptor.getSPSSODescriptor(""); + } + + private static UIInfo getUIInfo(EntityDescriptor ed) { + Extensions extensions = getSPSSODescriptorFromEntityDescriptor(ed).getExtensions(); + if (extensions == null) { + extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); + ed.getSPSSODescriptor("").setExtensions(extensions); + } + + UIInfo uiInfo; + if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { + uiInfo = (UIInfo) extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0); + } else { + uiInfo = openSamlObjects.buildDefaultInstanceOfType(UIInfo.class); + extensions.getUnknownXMLObjects().add(uiInfo); + } + return uiInfo; + } + + private static void removeUIInfo(EntityDescriptor ed) { + SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed, false); + if (spssoDescriptor != null) { + Extensions extensions = spssoDescriptor.getExtensions(); + if (extensions == null) { + return; + } + if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { + extensions.getUnknownXMLObjects().remove(extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0)); + } + } + } + + public static void setupACSs(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getAssertionConsumerServices() != null && representation.getAssertionConsumerServices().size() > 0) { + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); + for (AssertionConsumerServiceRepresentation acsRepresentation : representation.getAssertionConsumerServices()) { + AssertionConsumerService assertionConsumerService = openSamlObjects.buildDefaultInstanceOfType(AssertionConsumerService.class); + getSPSSODescriptorFromEntityDescriptor(ed).getAssertionConsumerServices().add(assertionConsumerService); + if (acsRepresentation.isMakeDefault()) { + assertionConsumerService.setIsDefault(true); + } + assertionConsumerService.setBinding(acsRepresentation.getBinding()); + assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); + assertionConsumerService.setIndex(acsRepresentation.getIndex()); + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); + } + } + + public static void setupContacts(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getContacts() != null && representation.getContacts().size() > 0) { + ed.getContactPersons().clear(); + for (ContactRepresentation contactRepresentation : representation.getContacts()) { + ContactPerson contactPerson = ((ContactPersonBuilder) openSamlObjects.getBuilderFactory().getBuilder(ContactPerson.DEFAULT_ELEMENT_NAME)).buildObject(); + + contactPerson.setType(contactRepresentation.getType()); + + GivenName givenName = openSamlObjects.buildDefaultInstanceOfType(GivenName.class); + givenName.setName(contactRepresentation.getName()); + contactPerson.setGivenName(givenName); + + EmailAddress emailAddress = openSamlObjects.buildDefaultInstanceOfType(EmailAddress.class); + emailAddress.setAddress(contactRepresentation.getEmailAddress()); + contactPerson.addEmailAddress(emailAddress); + + ed.addContactPerson(contactPerson); + } + } else { + ed.getContactPersons().clear(); + } + } + + public static void setupLogout(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // setup logout + if (representation.getLogoutEndpoints() != null && !representation.getLogoutEndpoints().isEmpty()) { + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); + for (LogoutEndpointRepresentation logoutEndpointRepresentation : representation.getLogoutEndpoints()) { + SingleLogoutService singleLogoutService = openSamlObjects.buildDefaultInstanceOfType(SingleLogoutService.class); + singleLogoutService.setBinding(logoutEndpointRepresentation.getBindingType()); + singleLogoutService.setLocation(logoutEndpointRepresentation.getUrl()); + + getSPSSODescriptorFromEntityDescriptor(ed).getSingleLogoutServices().add(singleLogoutService); + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); + } + } + + public static void setupOrganization(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getOrganization() != null && representation.getOrganization().getName() != null && representation.getOrganization().getDisplayName() != null && representation.getOrganization().getUrl() != null) { + OrganizationRepresentation organizationRepresentation = representation.getOrganization(); + Organization organization = openSamlObjects.buildDefaultInstanceOfType(Organization.class); + + OrganizationName organizationName = openSamlObjects.buildDefaultInstanceOfType(OrganizationName.class); + organizationName.setXMLLang("en"); + organizationName.setValue(organizationRepresentation.getName()); + organization.getOrganizationNames().add(organizationName); + + OrganizationDisplayName organizationDisplayName = openSamlObjects.buildDefaultInstanceOfType(OrganizationDisplayName.class); + organizationDisplayName.setXMLLang("en"); + organizationDisplayName.setValue(organizationRepresentation.getDisplayName()); + organization.getDisplayNames().add(organizationDisplayName); + + OrganizationURL organizationURL = openSamlObjects.buildDefaultInstanceOfType(OrganizationURL.class); + organizationURL.setXMLLang("en"); + organizationURL.setValue(organizationRepresentation.getUrl()); + organization.getURLs().add(organizationURL); + + ed.setOrganization(organization); + } else { + ed.setOrganization(null); + } + } + + public static void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getSecurityInfo() != null) { + SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); + if (securityInfoRepresentation.isAuthenticationRequestsSigned()) { + getSPSSODescriptorFromEntityDescriptor(ed).setAuthnRequestsSigned(true); + } + if (securityInfoRepresentation.isWantAssertionsSigned()) { + getSPSSODescriptorFromEntityDescriptor(ed).setWantAssertionsSigned(true); + } + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear()); + if (securityInfoRepresentation.isX509CertificateAvailable()) { + for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { + KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); + getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); + } + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent( spssoDescriptor -> { + spssoDescriptor.setAuthnRequestsSigned((Boolean) null); + spssoDescriptor.setWantAssertionsSigned((Boolean) null); + spssoDescriptor.getKeyDescriptors().clear(); + }); + } + } + + public static void setupSPSSODescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getServiceProviderSsoDescriptor() != null) { + SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed); + + spssoDescriptor.setSupportedProtocols(Collections.EMPTY_LIST); + if (!Strings.isNullOrEmpty(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum())) { + spssoDescriptor.setSupportedProtocols( + Arrays.stream(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum().split(",")).map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p.trim())).collect(Collectors.toList()) + ); + } + + spssoDescriptor.getNameIDFormats().clear(); + if (representation.getServiceProviderSsoDescriptor() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats().size() > 0) { + for (String nameidFormat : representation.getServiceProviderSsoDescriptor().getNameIdFormats()) { + NameIDFormat nameIDFormat = openSamlObjects.buildDefaultInstanceOfType(NameIDFormat.class); + + nameIDFormat.setFormat(nameidFormat); + + spssoDescriptor.getNameIDFormats().add(nameIDFormat); + } + } + } else { + ed.setRoleDescriptors(null); + } + } + + public static void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getMdui() != null) { + // TODO: check if we need more than a naive implementation + removeUIInfo(ed); + MduiRepresentation mduiRepresentation = representation.getMdui(); + + if (!Strings.isNullOrEmpty(mduiRepresentation.getDisplayName())) { + DisplayName displayName = openSamlObjects.buildDefaultInstanceOfType(DisplayName.class); + getUIInfo(ed).addDisplayName(displayName); + displayName.setValue(mduiRepresentation.getDisplayName()); + displayName.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getDisplayNames())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getInformationUrl())) { + InformationURL informationURL = openSamlObjects.buildDefaultInstanceOfType(InformationURL.class); + getUIInfo(ed).addInformationURL(informationURL); + informationURL.setValue(mduiRepresentation.getInformationUrl()); + informationURL.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getInformationURLs())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getPrivacyStatementUrl())) { + PrivacyStatementURL privacyStatementURL = openSamlObjects.buildDefaultInstanceOfType(PrivacyStatementURL.class); + getUIInfo(ed).addPrivacyStatementURL(privacyStatementURL); + privacyStatementURL.setValue(mduiRepresentation.getPrivacyStatementUrl()); + privacyStatementURL.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getPrivacyStatementURLs())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getDescription())) { + Description description = openSamlObjects.buildDefaultInstanceOfType(Description.class); + getUIInfo(ed).addDescription(description); + description.setValue(mduiRepresentation.getDescription()); + description.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getDescriptions())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getLogoUrl())) { + Logo logo = openSamlObjects.buildDefaultInstanceOfType(Logo.class); + getUIInfo(ed).addLogo(logo); + logo.setURL(mduiRepresentation.getLogoUrl()); + logo.setHeight(mduiRepresentation.getLogoHeight()); + logo.setWidth(mduiRepresentation.getLogoWidth()); + logo.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getLogos())); + } + } else { + removeUIInfo(ed); + } + } + + public static void setupRelyingPartyOverrides(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getRelyingPartyOverrides() != null || (representation.getAttributeRelease() != null && representation.getAttributeRelease().size() > 0)) { + // TODO: review if we need more than a naive implementation + getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); + getEntityAttributes(ed).getAttributes().addAll(entityService.getAttributeListFromEntityRepresentation(representation)); + } else { + getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); + } + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy index 9185a973b..35c015681 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy @@ -37,6 +37,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntitiesControllerTests extends Specification { + @Autowired + JPAEntityDescriptorServiceImpl serviceImpl + + @Autowired + UserService userService + def openSamlObjects = new OpenSamlObjects().with { init() it @@ -50,9 +56,6 @@ class EntitiesControllerTests extends Specification { initialize() it } - - @Autowired - UserService userService // This stub will spit out the results from the resolver instead of actually finding them in the DB @SpringBean @@ -62,14 +65,18 @@ class EntitiesControllerTests extends Specification { } @Subject - def controller = new EntitiesController( - openSamlObjects: openSamlObjects, - entityDescriptorService: new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), userService), - entityDescriptorRepository: edr - ) - - def mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + def controller + def mockMvc + def setup() { + controller = new EntitiesController() + controller.openSamlObjects = openSamlObjects + controller.entityDescriptorService = serviceImpl + controller.entityDescriptorRepository = edr + + mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + } + def 'GET /api/entities/test'() { when: def result = mockMvc.perform(get("/api/entities/test")) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy index 03ead4284..b7822e373 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy @@ -12,28 +12,45 @@ import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository import edu.internet2.tier.shibboleth.admin.ui.security.model.Group +import edu.internet2.tier.shibboleth.admin.ui.security.model.Role +import edu.internet2.tier.shibboleth.admin.ui.security.model.User +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils import groovy.json.JsonOutput import groovy.json.JsonSlurper +import org.skyscreamer.jsonassert.Customization +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode +import org.skyscreamer.jsonassert.ValueMatcher +import org.skyscreamer.jsonassert.comparator.CustomComparator +import org.skyscreamer.jsonassert.comparator.JSONCompareUtil +import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.support.RootBeanDefinition import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.context.annotation.ComponentScan import org.springframework.context.support.StaticApplicationContext import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContext import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.test.context.support.WithMockUser +import org.springframework.test.annotation.DirtiesContext +import org.springframework.test.annotation.Rollback import org.springframework.test.context.ContextConfiguration import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.web.client.RestTemplate @@ -42,11 +59,14 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupp import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver import spock.lang.Ignore +import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject import java.time.LocalDateTime +import javax.persistence.EntityManager + import static org.hamcrest.CoreMatchers.containsString import static org.springframework.http.MediaType.* import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* @@ -57,14 +77,32 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityDescriptorControllerTests extends Specification { + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Autowired + EntityManager entityManager + + @Autowired + EntityService entityService + + @Autowired + RoleRepository roleRepository + @Autowired + JPAEntityDescriptorServiceImpl service + + @Autowired + UserRepository userRepository + + @Autowired + UserService userService + RandomGenerator randomGenerator TestObjectGenerator generator def mapper - def service - - def entityDescriptorRepository = Mock(EntityDescriptorRepository) + def mockRestTemplate = Mock(RestTemplate) def openSamlObjects = new OpenSamlObjects().with { @@ -78,49 +116,63 @@ class EntityDescriptorControllerTests extends Specification { def controller Authentication authentication = Mock() - SecurityContext securityContext = Mock() - UserRepository userRepository = Mock() - RoleRepository roleRepository = Mock() - - UserService userService + SecurityContext securityContext = Mock() EntityDescriptorVersionService versionService = Mock() - IGroupService groupService = Mock() - + def setup() { generator = new TestObjectGenerator() randomGenerator = new RandomGenerator() mapper = new ObjectMapper() - userService = new UserService(roleRepository, userRepository) - service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), userService) - service.entityDescriptorRepository = entityDescriptorRepository + service.userService = userService service.groupService = groupService controller = new EntityDescriptorController(versionService) controller.openSamlObjects = openSamlObjects controller.entityDescriptorService = service - controller.restTemplate = mockRestTemplate mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); securityContext.getAuthentication() >> authentication SecurityContextHolder.setContext(securityContext) - Group defGroup = new Group(); - defGroup.setResourceId("testingGroup"); - defGroup.setName("For Tests"); - defGroup.setDefaultGroup(true); - Group.DEFAULT_GROUP = defGroup; + + if (roleRepository.count() == 0) { + def roles = [new Role().with { + name = 'ROLE_ADMIN' + it + }, new Role().with { + name = 'ROLE_USER' + it + }, new Role().with { + name = 'ROLE_NONE' + it + }] + roles.each { + roleRepository.save(it) + } + } + + Optional adminRole = roleRepository.findByName("ROLE_ADMIN") + User adminUser = new User(username: "admin", roles: [adminRole.get()], password: "foo") + userService.save(adminUser) + + Optional userRole = roleRepository.findByName("ROLE_USER") + User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") + userService.save(user) + entityManager.flush() + + EntityDescriptorConverstionUtils.setOpenSamlObjects(openSamlObjects) + EntityDescriptorConverstionUtils.setEntityService(entityService) } - + + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptors with empty repository as admin'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def emptyRecordsFromRepository = [].stream() + authentication.getName() >> 'admin' + def expectedEmptyListResponseBody = '[]' def expectedResponseContentType = APPLICATION_JSON def expectedHttpResponseStatus = status().isOk() @@ -129,124 +181,49 @@ class EntityDescriptorControllerTests extends Specification { def result = mockMvc.perform(get('/api/EntityDescriptors')) then: - //One call to the repo expected - 1 * entityDescriptorRepository.findAllStreamByCustomQuery() >> emptyRecordsFromRepository result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedEmptyListResponseBody)) - } - //todo review + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptors with 1 record in repository as admin'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find("testingGroup") >> Group.DEFAULT_GROUP - def expectedCreationDate = '2017-10-23T11:11:11' - def entityDescriptor = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) - def oneRecordFromRepository = [entityDescriptor].stream() - def version = entityDescriptor.hashCode() - def expectedOneRecordListResponseBody = """ - [ - { - "id": "uuid-1", - "serviceProviderName": "sp1", - "entityId": "eid1", - "serviceEnabled": true, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "organization": {}, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $version, - "createdBy": null, - "current": false, - "groupId": "testingGroup" - } - ] - """ - + authentication.getName() >> 'admin' + groupService.find("admingroup") >> Group.ADMIN_GROUP + def entityDescriptor = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + entityDescriptorRepository.save(entityDescriptor) + entityManager.flush() + def expectedResponseContentType = APPLICATION_JSON def expectedHttpResponseStatus = status().isOk() - when: + when: def result = mockMvc.perform(get('/api/EntityDescriptors')) - then: - //One call to the repo expected - 1 * entityDescriptorRepository.findAllStreamByCustomQuery() >> oneRecordFromRepository - result.andExpect(expectedHttpResponseStatus) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(expectedOneRecordListResponseBody, true)) - + then: + def mvcResult = result.andExpect(expectedHttpResponseStatus).andExpect(content().contentType(expectedResponseContentType)) + .andExpect(jsonPath("\$.[0].id").value("uuid-1")) + .andExpect(jsonPath("\$.[0].entityId").value("eid1")) + .andExpect(jsonPath("\$.[0].serviceEnabled").value(true)) + .andExpect(jsonPath("\$.[0].groupId").value("admingroup")) } - //todo review + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptors with 2 records in repository as admin'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find("testingGroup") >> Group.DEFAULT_GROUP - def expectedCreationDate = '2017-10-23T11:11:11' - def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) - def versionOne = entityDescriptorOne.hashCode() - def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', - serviceEnabled: false, - createdDate: LocalDateTime.parse(expectedCreationDate)) - def versionTwo = entityDescriptorTwo.hashCode() - def twoRecordsFromRepository = [entityDescriptorOne, entityDescriptorTwo].stream() - def expectedTwoRecordsListResponseBody = """ - [ - { - "id": "uuid-1", - "serviceProviderName": "sp1", - "entityId": "eid1", - "serviceEnabled": true, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "organization": {}, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $versionOne, - "createdBy": null, - "current": false, - "groupId": testingGroup - }, - { - "id": "uuid-2", - "serviceProviderName": "sp2", - "entityId": "eid2", - "serviceEnabled": false, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "organization": {}, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $versionTwo, - "createdBy": null, - "current": false, - "groupId": testingGroup - } - ] - """ + authentication.getName() >> 'admin' + groupService.find("admingroup") >> Group.ADMIN_GROUP + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', serviceEnabled: false) + + entityDescriptorRepository.save(entityDescriptorOne) + entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() + def expectedResponseContentType = APPLICATION_JSON def expectedHttpResponseStatus = status().isOk() @@ -254,35 +231,32 @@ class EntityDescriptorControllerTests extends Specification { def result = mockMvc.perform(get('/api/EntityDescriptors')) then: - //One call to the repo expected - 1 * entityDescriptorRepository.findAllStreamByCustomQuery() >> twoRecordsFromRepository result.andExpect(expectedHttpResponseStatus) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(expectedTwoRecordsListResponseBody, true)) - + .andExpect(content().contentType(expectedResponseContentType)) + .andExpect(jsonPath("\$.[0].id").value("uuid-1")) + .andExpect(jsonPath("\$.[0].entityId").value("eid1")) + .andExpect(jsonPath("\$.[0].serviceEnabled").value(true)) + .andExpect(jsonPath("\$.[0].groupId").value("admingroup")) + .andExpect(jsonPath("\$.[1].id").value("uuid-2")) + .andExpect(jsonPath("\$.[1].entityId").value("eid2")) + .andExpect(jsonPath("\$.[1].serviceEnabled").value(false)) + .andExpect(jsonPath("\$.[1].groupId").value("admingroup")) } - //todo review + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'POST /EntityDescriptor and successfully create new record'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find(null) >> null - def expectedCreationDate = '2017-10-23T11:11:11' + authentication.getName() >> 'admin' + groupService.find("admingroup") >> Group.ADMIN_GROUP + def expectedEntityId = 'https://shib' def expectedSpName = 'sp1' - def expectedUUID = 'uuid-1' def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/EntityDescriptor/$expectedUUID" - def entityDescriptor = new EntityDescriptor(resourceId: expectedUUID, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) - def version = entityDescriptor.hashCode() + def expectedResponseHeaderValue = "/api/EntityDescriptor/" def postedJsonBody = """ - { + { "serviceProviderName": "$expectedSpName", "entityId": "$expectedEntityId", "organization": {}, @@ -298,56 +272,23 @@ class EntityDescriptorControllerTests extends Specification { } """ - def expectedJsonBody = """ - { - "id": "$expectedUUID", - "serviceProviderName": "$expectedSpName", - "entityId": "$expectedEntityId", - "organization": {}, - "serviceEnabled": true, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $version, - "createdBy": null, - "current": false, - "groupId": "testingGroup" - } - """ - when: - def result = mockMvc.perform( - post('/api/EntityDescriptor') - .contentType(APPLICATION_JSON) - .content(postedJsonBody)) + def result = mockMvc.perform(post('/api/EntityDescriptor').contentType(APPLICATION_JSON).content(postedJsonBody)) then: - //Stub invocation of the repository returning null for non-existent record - 1 * entityDescriptorRepository.findByEntityID(expectedEntityId) >> null - - //Expect 1 invocation of repository save() with correct EntityDescriptor - 1 * entityDescriptorRepository.save({ - it.entityID == expectedEntityId && - it.serviceProviderName == expectedSpName && - it.serviceEnabled == true - }) >> entityDescriptor - result.andExpect(status().isCreated()) - .andExpect(content().json(expectedJsonBody, true)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - + .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) + .andExpect(jsonPath("\$.entityId").value("https://shib")) + .andExpect(jsonPath("\$.serviceEnabled").value(true)) + .andExpect(jsonPath("\$.groupId").value("admingroup")) } + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) def 'POST /EntityDescriptor as user disallows enabling'() { given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + authentication.getName() >> 'someUser' + def expectedEntityId = 'https://shib' def expectedSpName = 'sp1' @@ -381,17 +322,16 @@ class EntityDescriptorControllerTests extends Specification { } } + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'POST /EntityDescriptor record already exists'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def expectedEntityId = 'eid1' + authentication.getName() >> 'admin' + def postedJsonBody = """ { "serviceProviderName": "sp1", - "entityId": "$expectedEntityId", + "entityId": "eid1", "organization": null, "serviceEnabled": true, "createdDate": null, @@ -409,9 +349,13 @@ class EntityDescriptorControllerTests extends Specification { """ when: - //Stub invocation of the repository returning an existing record - 1 * entityDescriptorRepository.findByEntityID(expectedEntityId) >> new EntityDescriptor(entityID: expectedEntityId) - + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', serviceEnabled: false) + + entityDescriptorRepository.save(entityDescriptorOne) + entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() + then: try { def exceptionExpected = mockMvc.perform(post('/api/EntityDescriptor').contentType(APPLICATION_JSON).content(postedJsonBody)) @@ -420,251 +364,152 @@ class EntityDescriptorControllerTests extends Specification { e instanceof EntityIdExistsException == true } } - + + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptor/{resourceId} non-existent'() { - given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def providedResourceId = 'uuid-1' - when: - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> null + authentication.getName() >> 'admin' then: try { - def exceptionExpected = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) + def exceptionExpected = mockMvc.perform(get("/api/EntityDescriptor/uuid-1")) } catch (Exception e) { e instanceof EntityNotFoundException == true } } - //todo review + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptor/{resourceId} existing'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find(null) >> null - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) - def version = entityDescriptor.hashCode() - - def expectedJsonBody = """ - { - "id": "${providedResourceId}", - "serviceProviderName": "$expectedSpName", - "entityId": "$expectedEntityId", - "organization": {}, - "serviceEnabled": true, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $version, - "createdBy": null, - "current": false, - "groupId": "testingGroup" - } - """ - + authentication.getName() >> 'admin' + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() + when: - def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) - + def result = mockMvc.perform(get("/api/EntityDescriptor/uuid-1")) + then: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor - - result.andExpect(status().isOk()) - .andExpect(content().json(expectedJsonBody, true)) + .andExpect(jsonPath("\$.entityId").value("eid1")) + .andExpect(jsonPath("\$.serviceProviderName").value("sp1")) + .andExpect(jsonPath("\$.serviceEnabled").value(true)) + .andExpect(jsonPath("\$.groupId").value("admingroup")) } - - //todo review - def 'GET /EntityDescriptor/{resourceId} existing, owned by non-admin'() { + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) + def 'GET /EntityDescriptor/{resourceId} existing, validate group access'() { given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find(null) >> null - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate), - createdBy: 'someUser') - def version = entityDescriptor.hashCode() - - def expectedJsonBody = """ - { - "id": "${providedResourceId}", - "serviceProviderName": "$expectedSpName", - "entityId": "$expectedEntityId", - "organization": {}, - "serviceEnabled": true, - "createdDate": "$expectedCreationDate", - "modifiedDate": null, - "contacts": null, - "serviceProviderSsoDescriptor": null, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": null, - "version": $version, - "createdBy": "someUser", - "current": false, - "groupId": "testingGroup" - } - """ - + authentication.getName() >> 'someUser' + Group g = userService.getCurrentUserGroup() + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: g) + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', serviceEnabled: false, group: Group.ADMIN_GROUP) + + entityDescriptorRepository.save(entityDescriptorOne) + entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() + when: - def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) + def result = mockMvc.perform(get("/api/EntityDescriptor/uuid-1")) then: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor - - - result.andExpect(status().isOk()).andExpect(content().json(expectedJsonBody, true)) + result.andExpect(status().isOk()) + .andExpect(jsonPath("\$.entityId").value("eid1")) + .andExpect(jsonPath("\$.serviceProviderName").value("sp1")) + .andExpect(jsonPath("\$.serviceEnabled").value(true)) + .andExpect(jsonPath("\$.groupId").value("someUser")) } + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) def 'GET /EntityDescriptor/{resourceId} existing, owned by some other user'() { - given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate), - createdBy: 'someOtherUser') - when: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + authentication.getName() >> 'someUser' + Group g = userService.getCurrentUserGroup() + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: g) + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', serviceEnabled: false, group: Group.ADMIN_GROUP) + + entityDescriptorRepository.save(entityDescriptorOne) + entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() then: try { - def exceptionExpected = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) + def exceptionExpected = mockMvc.perform(get("/api/EntityDescriptor/uuid-2")) } catch (Exception e) { e instanceof ForbiddenException == true } } + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'GET /EntityDescriptor/{resourceId} existing (xml)'() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) - entityDescriptor.setElementLocalName("EntityDescriptor") - entityDescriptor.setNamespacePrefix("md") - entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + authentication.getName() >> 'admin' + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + entityDescriptorOne.setElementLocalName("EntityDescriptor") + entityDescriptorOne.setNamespacePrefix("md") + entityDescriptorOne.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() def expectedXML = """ """ + xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="eid1"/>""" when: - def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId") - .accept(APPLICATION_XML)) + def result = mockMvc.perform(get("/api/EntityDescriptor/uuid-1").accept(APPLICATION_XML)) then: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor - - - result.andExpect(status().isOk()) - .andExpect(content().xml(expectedXML)) + result.andExpect(status().isOk()).andExpect(content().xml(expectedXML)) } + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) def 'GET /EntityDescriptor/{resourceId} existing (xml), user-owned'() { given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate), - createdBy: 'someUser') - entityDescriptor.setElementLocalName("EntityDescriptor") - entityDescriptor.setNamespacePrefix("md") - entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") - entityDescriptor.setGroup(Group.DEFAULT_GROUP) + authentication.getName() >> 'someUser' + Group g = userService.getCurrentUserGroup() + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: g) + entityDescriptorOne.setElementLocalName("EntityDescriptor") + entityDescriptorOne.setNamespacePrefix("md") + entityDescriptorOne.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() def expectedXML = """ """ + xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="eid1"/>""" when: - def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId").accept(APPLICATION_XML)) + def result = mockMvc.perform(get("/api/EntityDescriptor/uuid-1").accept(APPLICATION_XML)) then: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor - result.andExpect(status().isOk()).andExpect(content().xml(expectedXML)) - } + } + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) def 'GET /EntityDescriptor/{resourceId} existing (xml), other user-owned'() { - given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def expectedCreationDate = '2017-10-23T11:11:11' - def providedResourceId = 'uuid-1' - def expectedSpName = 'sp1' - def expectedEntityId = 'eid1' - - def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate), - createdBy: 'someOtherUser') - entityDescriptor.setElementLocalName("EntityDescriptor") - entityDescriptor.setNamespacePrefix("md") - entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") - when: - //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor - + authentication.getName() >> 'someUser' + Group g = Group.ADMIN_GROUP + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: g) + entityDescriptorOne.setElementLocalName("EntityDescriptor") + entityDescriptorOne.setNamespacePrefix("md") + entityDescriptorOne.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() + then: try { def exceptionExpected = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId").accept(APPLICATION_XML)) @@ -674,14 +519,13 @@ class EntityDescriptorControllerTests extends Specification { } } - - //todo review + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def "POST /EntityDescriptor handles XML happily"() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + authentication.getName() >> 'admin' + groupService.find("admingroup") >> Group.ADMIN_GROUP + def postedBody = ''' @@ -701,62 +545,32 @@ class EntityDescriptorControllerTests extends Specification { ''' - def spName = randomGenerator.randomString() - - def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(postedBody.bytes)) - - 1 * entityDescriptorRepository.findByEntityID(_) >> null - 1 * entityDescriptorRepository.save(_) >> expectedEntityDescriptor - - def expectedJson = """ -{ - "version": ${expectedEntityDescriptor.hashCode()}, - "id": "${expectedEntityDescriptor.resourceId}", - "serviceProviderName": null, - "entityId": "http://test.scaldingspoon.org/test1", - "organization": {}, - "contacts": null, - "serviceProviderSsoDescriptor": { - "protocolSupportEnum": "SAML 2", - "nameIdFormats": [ - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - ] - }, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": [ - { - "locationUrl": "https://test.scaldingspoon.org/test1/acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", - "makeDefault": false - } - ], - "serviceEnabled": false, - "createdDate": null, - "modifiedDate": null, - "attributeRelease": [ - "givenName", - "employeeNumber" - ], - "createdBy": null, - "current": false -} -""" + def expectedResponseHeader = 'Location' + def expectedResponseHeaderValue = "/api/EntityDescriptor/" when: - def result = mockMvc.perform(post("/api/EntityDescriptor") - .contentType(APPLICATION_XML) - .content(postedBody) - .param("spName", spName)) - + def spName = randomGenerator.randomString() + def result = mockMvc.perform(post("/api/EntityDescriptor").contentType(APPLICATION_XML).content(postedBody).param("spName", spName)) then: result.andExpect(status().isCreated()) - .andExpect(content().json(expectedJson, false)) + .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) + .andExpect(jsonPath("\$.entityId").value("http://test.scaldingspoon.org/test1")) + .andExpect(jsonPath("\$.serviceEnabled").value(false)) + .andExpect(jsonPath("\$.groupId").value("admingroup")) + .andExpect(jsonPath("\$.serviceProviderSsoDescriptor.protocolSupportEnum").value("SAML 2")) + .andExpect(jsonPath("\$.serviceProviderSsoDescriptor.nameIdFormats[0]").value("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")) + .andExpect(jsonPath("\$.assertionConsumerServices[0].binding").value("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")) + .andExpect(jsonPath("\$.assertionConsumerServices[0].makeDefault").value(false)) + .andExpect(jsonPath("\$.assertionConsumerServices[0].locationUrl").value("https://test.scaldingspoon.org/test1/acs")) + } + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def "POST /EntityDescriptor returns error for duplicate entity id"() { - given: + when: + authentication.getName() >> 'admin' def postedBody = ''' @@ -777,13 +591,13 @@ class EntityDescriptorControllerTests extends Specification { ''' def spName = randomGenerator.randomString() - - def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(postedBody.bytes)) - 0 * entityDescriptorRepository.save(_) - - when: - 1 * entityDescriptorRepository.findByEntityID(expectedEntityDescriptor.entityID) >> expectedEntityDescriptor - + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true) + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'http://test.scaldingspoon.org/test1', serviceProviderName: 'sp2', serviceEnabled: false) + + entityDescriptorRepository.save(entityDescriptorOne) + entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() + then: try { def exceptionExpected = mockMvc.perform(post("/api/EntityDescriptor").contentType(APPLICATION_XML).content(postedBody).param("spName", spName)) @@ -793,196 +607,104 @@ class EntityDescriptorControllerTests extends Specification { } } - @Ignore("until we handle the workaround for SHIBUI-1237") - def "POST /EntityDescriptor handles x-www-form-urlencoded happily"() { - given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def postedMetadataUrl = "http://test.scaldingspoon.org/test1" - def restXml = ''' - - - - - internal - - - givenName - employeeNumber - - - - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - - -''' - - def spName = randomGenerator.randomString() - - def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(restXml.bytes)) - - 1 * mockRestTemplate.getForObject(_, _) >> restXml.bytes - 1 * entityDescriptorRepository.findByEntityID(_) >> null - 1 * entityDescriptorRepository.save(_) >> expectedEntityDescriptor - - def expectedJson = """ -{ - "version": ${expectedEntityDescriptor.hashCode()}, - "id": "${expectedEntityDescriptor.resourceId}", - "serviceProviderName": null, - "entityId": "http://test.scaldingspoon.org/test1", - "organization": null, - "contacts": null, - "mdui": null, - "serviceProviderSsoDescriptor": { - "protocolSupportEnum": "SAML 2", - "nameIdFormats": [ - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - ] - }, - "logoutEndpoints": null, - "securityInfo": null, - "assertionConsumerServices": [ - { - "locationUrl": "https://test.scaldingspoon.org/test1/acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", - "makeDefault": false - } - ], - "serviceEnabled": false, - "createdDate": null, - "modifiedDate": null, - "relyingPartyOverrides": {}, - "attributeRelease": [ - "givenName", - "employeeNumber" - ], - "createdBy": null -} -""" - - when: - def result = mockMvc.perform(post("/api/EntityDescriptor") - .contentType(APPLICATION_FORM_URLENCODED) - .param("metadataUrl", postedMetadataUrl) - .param("spName", spName)) - - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(expectedJson, true)) - } - + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def "PUT /EntityDescriptor updates entity descriptors properly as admin"() { given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - groupService.find(null) >> null - def entityDescriptor = generator.buildEntityDescriptor() - def updatedEntityDescriptor = generator.buildEntityDescriptor() - updatedEntityDescriptor.resourceId = entityDescriptor.resourceId - def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) - updatedEntityDescriptorRepresentation.version = entityDescriptor.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) - - def resourceId = entityDescriptor.resourceId - - 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor - 1 * entityDescriptorRepository.save(_) >> updatedEntityDescriptor + authentication.getName() >> 'admin' + + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', serviceEnabled: false, group: Group.ADMIN_GROUP) + + entityDescriptorTwo = entityDescriptorRepository.save(entityDescriptorTwo) + entityManager.flush() + entityManager.clear() + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(entityDescriptorTwo) + updatedEntityDescriptorRepresentation.setServiceProviderName("newName") + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) + when: - def result = mockMvc.perform( - put("/api/EntityDescriptor/$resourceId") - .contentType(APPLICATION_JSON) - .content(postedJsonBody)) + def result = mockMvc.perform(put("/api/EntityDescriptor/uuid-2").contentType(APPLICATION_JSON).content(postedJsonBody)) then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedEntityDescriptor.hashCode()] result.andExpect(status().isOk()) - .andExpect(content().json(JsonOutput.toJson(expectedJson), true)) + .andExpect(jsonPath("\$.entityId").value("eid2")) + .andExpect(jsonPath("\$.serviceEnabled").value(false)) + .andExpect(jsonPath("\$.groupId").value("admingroup")) + .andExpect(jsonPath("\$.serviceProviderName").value("newName")) } - def "PUT /EntityDescriptor disallows user from enabling"() { + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) + def "PUT /EntityDescriptor disallows non-admin user from enabling"() { given: - def username = 'someUser' - def role = 'ROLE_USER' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def entityDescriptor = generator.buildEntityDescriptor() - entityDescriptor.serviceEnabled = false - def updatedEntityDescriptor = generator.buildEntityDescriptor() - updatedEntityDescriptor.serviceEnabled = true - updatedEntityDescriptor.resourceId = entityDescriptor.resourceId - def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) - updatedEntityDescriptorRepresentation.version = entityDescriptor.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) - - def resourceId = entityDescriptor.resourceId - 0 * entityDescriptorRepository.save(_) >> updatedEntityDescriptor + authentication.getName() >> 'someUser' + Group g = userService.getCurrentUserGroup() + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: false, group: g) + entityDescriptorOne = entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() when: - 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor + entityDescriptorOne.serviceEnabled = true + entityDescriptorOne.resourceId = 'uuid-1' + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(entityDescriptorOne) + updatedEntityDescriptorRepresentation.version = entityDescriptorOne.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) then: try { - def exceptionExpected = mockMvc.perform(put("/api/EntityDescriptor/$resourceId").contentType(APPLICATION_JSON).content(postedJsonBody)) + def exceptionExpected = mockMvc.perform(put("/api/EntityDescriptor/uuid-1").contentType(APPLICATION_JSON).content(postedJsonBody)) } catch (Exception e) { e instanceof ForbiddenException == true } } - def "PUT /EntityDescriptor denies the request if the PUTing user is not an ADMIN and not the createdBy user"() { + @Rollback + @WithMockUser(value = "someUser", roles = ["USER"]) + def "PUT /EntityDescriptor denies the request if the PUTing user is not an ADMIN and not the createdBy user"() { given: - def username = 'someUser' - def role = 'ROLE_USERN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def entityDescriptor = generator.buildEntityDescriptor() - entityDescriptor.createdBy = 'someoneElse' - def updatedEntityDescriptor = generator.buildEntityDescriptor() - updatedEntityDescriptor.createdBy = 'someoneElse' - updatedEntityDescriptor.resourceId = entityDescriptor.resourceId - def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) - def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) - def resourceId = entityDescriptor.resourceId - + authentication.getName() >> 'someUser' + Group g = userService.getCurrentUserGroup() + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: g) + entityDescriptorOne = entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() + when: - 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor + entityDescriptorOne.serviceProviderName = 'foo' + entityDescriptorOne.resourceId = 'uuid-1' + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(entityDescriptorOne) + updatedEntityDescriptorRepresentation.version = entityDescriptorOne.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) then: try { - def exceptionExpected = mockMvc.perform(put("/api/EntityDescriptor/$resourceId").contentType(APPLICATION_JSON).content(postedJsonBody)) + def exceptionExpected = mockMvc.perform(put("/api/EntityDescriptor/uuid-1").contentType(APPLICATION_JSON).content(postedJsonBody)) } catch (Exception e) { e instanceof ForbiddenException == true } } + @Rollback + @WithMockUser(value = "admin", roles = ["ADMIN"]) def "PUT /EntityDescriptor throws a concurrent mod exception if the version numbers don't match"() { - given: - def username = 'admin' - def role = 'ROLE_ADMIN' - authentication.getName() >> username - userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) - def entityDescriptor = generator.buildEntityDescriptor() - def updatedEntityDescriptor = generator.buildEntityDescriptor() - updatedEntityDescriptor.resourceId = entityDescriptor.resourceId - def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) - def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) - - def resourceId = entityDescriptor.resourceId + given: + authentication.getName() >> 'admin' + + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, group: Group.ADMIN_GROUP) + entityDescriptorOne = entityDescriptorRepository.save(entityDescriptorOne) + entityManager.flush() when: - 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor - + entityDescriptorOne.serviceProviderName = 'foo' + entityDescriptorOne.resourceId = 'uuid-1' + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(entityDescriptorOne) + + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) + then: try { def exception = mockMvc.perform(put("/api/EntityDescriptor/$resourceId").contentType(APPLICATION_JSON).content(postedJsonBody)) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy index bfeacf55d..3fb7a3f21 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy @@ -12,6 +12,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import org.apache.lucene.analysis.Analyzer @@ -22,6 +23,7 @@ import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration @@ -63,7 +65,8 @@ class EntityDescriptorRepositoryTest extends Specification { it } - def service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), new UserService(roleRepository, userRepository)) + @Autowired + EntityDescriptorService service def "SHIBUI-553.2"() { when: diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy index a0ba13aca..99ff22481 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy @@ -10,10 +10,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetada import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects -import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import org.springframework.beans.factory.annotation.Autowired @@ -44,15 +42,7 @@ class MetadataResolverRepositoryTests extends Specification { EntityManager entityManager @Autowired - OpenSamlObjects openSamlObjects - - @Autowired - RoleRepository roleRepository - - @Autowired - UserRepository userRepository - - def service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), new UserService(roleRepository, userRepository)) + EntityDescriptorService service def "test persisting a metadata resolver"() { when: diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy index 7402cb098..19d0e4689 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy @@ -16,6 +16,8 @@ import edu.internet2.tier.shibboleth.admin.ui.service.FileCheckingFileWritingSer import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils + import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -34,38 +36,36 @@ import spock.lang.Specification @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityDescriptorFilesScheduledTasksTests extends Specification { + @Autowired + OpenSamlObjects openSamlObjects + + @Autowired + IGroupService groupService + def tempPath = "/tmp/shibui" def directory - @Autowired - OpenSamlObjects openSamlObjects - def entityDescriptorRepository = Mock(EntityDescriptorRepository) def entityDescriptorFilesScheduledTasks - def service - def randomGenerator - - @Autowired - RoleRepository roleRepository - - @Autowired - UserRepository userRepository - - @Autowired - IGroupService groupService + + + def service def setup() { randomGenerator = new RandomGenerator() tempPath = tempPath + randomGenerator.randomRangeInt(10000, 20000) - service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), new UserService(roleRepository, userRepository)) - service.groupService = groupService + EntityDescriptorConverstionUtils.setOpenSamlObjects(openSamlObjects) entityDescriptorFilesScheduledTasks = new EntityDescriptorFilesScheduledTasks(tempPath, entityDescriptorRepository, openSamlObjects, new FileCheckingFileWritingService()) directory = new File(tempPath) directory.mkdir() + + service = new JPAEntityDescriptorServiceImpl() + service.openSamlObjects = openSamlObjects + service.groupService = groupService } def "generateEntityDescriptorFiles properly generates a file from an Entity Descriptor"() { @@ -127,7 +127,7 @@ class EntityDescriptorFilesScheduledTasksTests extends Specification { http://test.example.org - ''' + ''' def entityDescriptor = service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { it.entityId = 'http://test.example.org/test1' diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryIntegrationTests.groovy index 02fde7269..eeae13328 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryIntegrationTests.groovy @@ -5,6 +5,8 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.JsonSchemaComponents import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor import edu.internet2.tier.shibboleth.admin.ui.jsonschema.LowLevelJsonSchemaValidator import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group + import org.springframework.core.io.DefaultResourceLoader import org.springframework.core.io.ResourceLoader import org.springframework.mock.http.MockHttpInputMessage @@ -14,31 +16,33 @@ import spock.lang.Specification import java.time.LocalDateTime class AuxiliaryIntegrationTests extends Specification { - @Shared OpenSamlObjects openSamlObjects = new OpenSamlObjects().with { it.init() it } - @Shared - EntityDescriptorService entityDescriptorService - - @Shared + JPAEntityDescriptorServiceImpl entityDescriptorService ObjectMapper objectMapper - - @Shared ResourceLoader resourceLoader void setup() { - this.entityDescriptorService = new JPAEntityDescriptorServiceImpl(openSamlObjects, null, null) - this.objectMapper = new ObjectMapper() - this.resourceLoader = new DefaultResourceLoader() + entityDescriptorService = new JPAEntityDescriptorServiceImpl() + entityDescriptorService.openSamlObjects = openSamlObjects + objectMapper = new ObjectMapper() + resourceLoader = new DefaultResourceLoader() } def "SHIBUI-1723: after enabling saved entity descriptor, it should still have valid xml"() { given: + def group = new Group().with { + it.name = "foo" + it.resourceId = "foo" + it + } def entityDescriptor = openSamlObjects.unmarshalFromXml(this.class.getResource('/metadata/SHIBUI-1723-1.xml').bytes) as EntityDescriptor - def entityDescriptorRepresentation = this.entityDescriptorService.createRepresentationFromDescriptor(entityDescriptor).with { + entityDescriptor.group = group + + def entityDescriptorRepresentation = entityDescriptorService.createRepresentationFromDescriptor(entityDescriptor).with { it.serviceProviderName = 'testme' it.contacts = [] it.securityInfo.x509Certificates[0].name = 'testcert' @@ -47,7 +51,7 @@ class AuxiliaryIntegrationTests extends Specification { it.setModifiedDate(LocalDateTime.now()) it } - def json = this.objectMapper.writeValueAsString(entityDescriptorRepresentation) + def json = objectMapper.writeValueAsString(entityDescriptorRepresentation) def schemaUri = edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.metadataSourcesSchema(new JsonSchemaComponentsConfiguration().jsonSchemaResourceLocationRegistry(this.resourceLoader, this.objectMapper)).uri when: diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index 11da9c2ef..4ec0b37b8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -27,12 +27,15 @@ import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils + import org.opensaml.saml.ext.saml2mdattr.EntityAttributes import org.skyscreamer.jsonassert.JSONAssert import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.json.JacksonTester import org.springframework.context.annotation.PropertySource +import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input @@ -43,47 +46,34 @@ import spock.lang.Specification @ContextConfiguration(classes=[CoreShibUiConfiguration, CustomPropertiesConfiguration]) @SpringBootTest(classes = ShibbolethUiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) @PropertySource("classpath:application.yml") -class JPAEntityDescriptorServiceImplTests extends Specification { - +@DirtiesContext +class JPAEntityDescriptorServiceImplTests extends Specification { + @Autowired + EntityService entityService; + @Autowired - CustomPropertiesConfiguration customPropertiesConfiguration + JPAEntityDescriptorServiceImpl service def testObjectGenerator - + OpenSamlObjects openSamlObjects = new OpenSamlObjects().with { init() it } - - JPAEntityDescriptorServiceImpl service - + JacksonTester jacksonTester - ObjectMapper mapper - RandomGenerator generator - - @Autowired - RoleRepository roleRepository - - @Autowired - UserRepository userRepository - - @Autowired - IGroupService groupService - def setup() { - service = new JPAEntityDescriptorServiceImpl(openSamlObjects, - new JPAEntityServiceImpl(openSamlObjects, new AttributeUtility(openSamlObjects), customPropertiesConfiguration), new UserService(roleRepository, userRepository)) - service.groupService = groupService + def setup() { mapper = new ObjectMapper() JacksonTester.initFields(this, mapper) generator = new RandomGenerator() testObjectGenerator = new TestObjectGenerator() + EntityDescriptorConverstionUtils.openSamlObjects = openSamlObjects + EntityDescriptorConverstionUtils.entityService = entityService } - - def "simple Entity Descriptor"() { when: def expected = ''' adminRole = roleRepository.findByName("ROLE_ADMIN") + User adminUser = new User(username: "admin", roles: [adminRole.get()], password: "foo") + userService.save(adminUser) - User u = new User() - u.setUsername("foo") - u.setGroup(gb) - u.setPassword("pass") - userRepository.save(u) + Optional userRole = roleRepository.findByName("ROLE_USER") + User user = new User(username: "someUser", roles:[userRole.get()], password: "foo", group: gb) + userService.save(user) } - @WithMockUser(value = "foo", roles = ["USER"]) + @WithMockUser(value = "someUser", roles = ["USER"]) + @Rollback def "When creating Entity Descriptor, ED is assigned to the user's group"() { given: User current = userService.getCurrentUser() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrapTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrapTests.groovy index 0a5db2bcf..8360bf8d5 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrapTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/UserBootstrapTests.groovy @@ -34,6 +34,10 @@ class UserBootstrapTests extends Specification { @Autowired RoleRepository roleRepository + def setup() { + roleRepository.deleteAll(); + } + def "simple test"() { setup: shibUIConfiguration.roles = [] diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryJPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy similarity index 94% rename from backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryJPAEntityDescriptorServiceImplTests.groovy rename to backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy index f78f8d354..03bbefd77 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/AuxiliaryJPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy @@ -1,4 +1,7 @@ -package edu.internet2.tier.shibboleth.admin.ui.service +package edu.internet2.tier.shibboleth.admin.ui.util + +import org.opensaml.saml.common.xml.SAMLConstants +import org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson import edu.internet2.tier.shibboleth.admin.ui.domain.Description @@ -22,63 +25,31 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects -import org.opensaml.saml.common.xml.SAMLConstants -import org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConverstionUtils import spock.lang.Shared import spock.lang.Specification import spock.lang.Unroll -class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { - @Shared - OpenSamlObjects openSAMLObjects = new OpenSamlObjects().with { - it.init() - it - } +class EntityDescriptorConversionUtilsTests extends Specification { @Shared - JPAEntityDescriptorServiceImpl entityDescriptorService - - void setup() { - entityDescriptorService = new JPAEntityDescriptorServiceImpl(openSAMLObjects, null, null) - } - - def "simple test"() { - assert true - } - - // this is a stub to build out the DataFields - def "pretest"() { - given: - def dataField = new Data.DataField( - method: 'setupLogout', - description: 'no change', - representation: new EntityDescriptorRepresentation(), - starter: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class), - expected: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class) - ) - - when: - def (expected, starter) = [dataField.expected, dataField.starter] - expected.setResourceId(starter.getResourceId()) - entityDescriptorService."${dataField.method}"(starter, dataField.representation) - - then: - assert expected == starter - } - - @Unroll - def "test #method(#description)"() { - setup: - expected.setResourceId(starter.getResourceId()) - entityDescriptorService."$method"(starter, representation) + def OpenSamlObjects openSAMLObjects + + @Shared + def EntityDescriptorConverstionUtils utilsUnderTest - expect: - assert starter == expected + def setup() { + openSAMLObjects = new OpenSamlObjects().with { + it.init() + it + } - where: - [method, description, representation, starter, expected] << Data.getData(openSAMLObjects) + utilsUnderTest = new EntityDescriptorConverstionUtils().with { + it.openSamlObjects = openSAMLObjects + it + } } - + def "test createKeyDescriptor, single type"() { given: def expectedXml = ''' @@ -92,12 +63,12 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { expected.name = 'testName' when: - def keyDescriptor = entityDescriptorService.createKeyDescriptor('testName', 'signing', 'testValue') + def keyDescriptor = EntityDescriptorConverstionUtils.createKeyDescriptor('testName', 'signing', 'testValue') then: assert keyDescriptor == expected } - + def "test createKeyDescriptor, both type"() { given: def expectedXml = ''' @@ -111,7 +82,7 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { expected.name = 'testName' when: - def keyDescriptor = entityDescriptorService.createKeyDescriptor('testName', 'both', 'testValue') + def keyDescriptor = EntityDescriptorConverstionUtils.createKeyDescriptor('testName', 'both', 'testValue') def x = openSAMLObjects.marshalToXmlString(keyDescriptor) then: assert keyDescriptor == expected @@ -119,16 +90,28 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { def 'test createKeyDescriptor equality'() { when: - def key1 = entityDescriptorService.createKeyDescriptor('test', 'signing', 'test') - def key2 = entityDescriptorService.createKeyDescriptor('test', 'signing', 'test') + def key1 = EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test') + def key2 = EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test') then: assert key1.equals(key2) } + + @Unroll + def "test #method(#description)"() { + setup: + expected.setResourceId(starter.getResourceId()) + EntityDescriptorConverstionUtils."$method"(starter, representation) + + expect: + assert starter == expected + + where: + [method, description, representation, starter, expected] << Data.getData(openSAMLObjects) + } static class Data { static def getData(OpenSamlObjects openSAMLObjects) { - JPAEntityDescriptorServiceImpl entityDescriptorService = new JPAEntityDescriptorServiceImpl(openSAMLObjects, null, null) def data = [] data << new DataField( @@ -621,7 +604,7 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { expected: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) it } ) @@ -645,7 +628,7 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { starter: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) it } ) @@ -654,8 +637,8 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { expected: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test2', 'encryption', 'test2')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test2', 'encryption', 'test2')) it } ) @@ -678,8 +661,8 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { starter: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test2', 'encryption', 'test2')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test2', 'encryption', 'test2')) it } ) @@ -688,7 +671,7 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { expected: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test2', 'encryption', 'test2')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test2', 'encryption', 'test2')) it } ) @@ -708,8 +691,8 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { starter: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'encryption', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'encryption', 'test')) it } ) @@ -729,8 +712,8 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { starter: openSAMLObjects.buildDefaultInstanceOfType(EntityDescriptor.class).with { it.getRoleDescriptors().add( openSAMLObjects.buildDefaultInstanceOfType(SPSSODescriptor.class).with { - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'signing', 'test')) - it.addKeyDescriptor(entityDescriptorService.createKeyDescriptor('test', 'encryption', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'signing', 'test')) + it.addKeyDescriptor(EntityDescriptorConverstionUtils.createKeyDescriptor('test', 'encryption', 'test')) it } ) @@ -1108,11 +1091,9 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { it } ) - return data } - static class DataField implements Iterable { String method String description @@ -1126,4 +1107,4 @@ class AuxiliaryJPAEntityDescriptorServiceImplTests extends Specification { } } } -} +} \ No newline at end of file