Skip to content

Commit

Permalink
SHIBUI-2003
Browse files Browse the repository at this point in the history
changed to group and users as owners and owned
  • Loading branch information
chasegawa committed Aug 9, 2021
1 parent d43407e commit 154cea4
Show file tree
Hide file tree
Showing 36 changed files with 1,081 additions and 613 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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.util.ModelRepresentationConversions

Expand Down Expand Up @@ -47,14 +48,17 @@ class DevConfig {
MetadataResolverRepository metadataResolverRepository,
RoleRepository roleRepository,
EntityDescriptorRepository entityDescriptorRepository,
OpenSamlObjects openSamlObjects) {
OpenSamlObjects openSamlObjects,
IGroupService groupService) {

this.userRepository = adminUserRepository
this.metadataResolverRepository = metadataResolverRepository
this.roleRepository = roleRepository
this.entityDescriptorRepository = entityDescriptorRepository
this.openSamlObjects = openSamlObjects
this.groupsRepository = groupsRepository

groupService.ensureAdminGroupExists()
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.MetadataProvidersScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.GroupUpdatedEntityListener;
import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.UserUpdatedEntityListener;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository;
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.DefaultMetadataResolversPositionOrderContainerService;
import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService;
Expand Down Expand Up @@ -201,8 +206,8 @@ public ModelRepresentationConversions modelRepresentationConversions() {
}

@Bean
public UserService userService(RoleRepository roleRepository, UserRepository userRepository) {
return new UserService(roleRepository, userRepository);
public UserService userService(IGroupService groupService, OwnershipRepository ownershipRepository, RoleRepository roleRepository, UserRepository userRepository) {
return new UserService(groupService, ownershipRepository, roleRepository, userRepository);
}

@Bean
Expand All @@ -216,4 +221,18 @@ public EntityDescriptorConversionUtils EntityDescriptorConverstionUtilsInit(Enti
EntityDescriptorConversionUtils.setOpenSamlObjects(oso);
return new EntityDescriptorConversionUtils();
}

@Bean
public GroupUpdatedEntityListener groupUpdatedEntityListener(OwnershipRepository repo) {
GroupUpdatedEntityListener listener = new GroupUpdatedEntityListener();
listener.init(repo);
return listener;
}

@Bean
public UserUpdatedEntityListener userUpdatedEntityListener(OwnershipRepository repo, GroupsRepository groupRepo) {
UserUpdatedEntityListener listener = new UserUpdatedEntityListener();
listener.init(repo, groupRepo);
return listener;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration.auto;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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.Ownership;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository;
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;

/**
* After the context loads, do any needed migration tasks
*/
@Component
public class MigrationTasksContextLoadedListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private EntityDescriptorRepository entityDescriptorRepository;

@Autowired
private IGroupService groupService;

@Autowired
private OwnershipRepository ownershipRepository;

@Autowired
private UserRepository userRepository;

@Autowired
private UserService userService;

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
doshibui_1740_migration(); // do first
}

@Transactional
private void doshibui_1740_migration() {
groupService.ensureAdminGroupExists(); // do first

// SHIBUI-1740: Adding admin group to all existing entity descriptors that do not have a group already.
// the ADMIN_GROUP has already been setup (just above)
try {
entityDescriptorRepository.findAllByIdOfOwnerIsNull().forEach(ed -> {
ed.setIdOfOwner(Group.ADMIN_GROUP.getOwnerId());
ed = entityDescriptorRepository.saveAndFlush(ed);
ownershipRepository.saveAndFlush(new Ownership(Group.ADMIN_GROUP, 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
}

userRepository.findAll().forEach(user -> {
if (user.getGroupId() == null) {
userService.save(user); // this will ensure group is set as the default user group
}
});

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -39,14 +40,17 @@ public class DangerController {
@Autowired
private MetadataResolversPositionOrderContainerRepository metadataResolversPositionOrderContainerRepository;

@Autowired
private OwnershipRepository ownershipRepository;

@Transactional
@GetMapping
public ResponseEntity<?> wipeOut() {
edRepo.findAll().forEach(ed -> {
try {
ed.setServiceEnabled(false);
edRepo.save(ed);
groupService.removeEntityFromGroup(ed);
ownershipRepository.deleteEntriesForOwnedObject(ed);
entityDescriptorService.delete(ed.getResourceId());
}
catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.google.common.collect.Lists;

import edu.internet2.tier.shibboleth.admin.ui.security.model.Group;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.OwnableType;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -36,7 +38,7 @@
@Entity
@EqualsAndHashCode(callSuper = true)
@Audited
public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor {
public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor, Ownable {
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "entitydesc_addlmetdatlocations_id")
@OrderColumn
Expand All @@ -61,18 +63,15 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml

private String entityID;

@ManyToOne
@JoinColumn(name = "group_resource_id")
@EqualsAndHashCode.Exclude
@Setter
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
private Group group;

private String localId;

@OneToOne(cascade = CascadeType.ALL)
private Organization organization;

@Getter
@Setter
private String idOfOwner;

@OneToOne(cascade = CascadeType.ALL)
@NotAudited
private PDPDescriptor pdpDescriptor;
Expand Down Expand Up @@ -144,10 +143,6 @@ public IDPSSODescriptor getIDPSSODescriptor(String s) {
.orElse(null);
}

public Group getGroup() {
return group == null ? Group.ADMIN_GROUP : group;
}

@Transient
public Optional<SPSSODescriptor> getOptionalSPSSODescriptor() {
return this.getOptionalSPSSODescriptor("");
Expand Down Expand Up @@ -302,4 +297,12 @@ public String toString() {
.add("id", id)
.toString();
}

public String getObjectId() {
return entityID;
}

public OwnableType getOwnableType() {
return OwnableType.ENTITY_DESCRIPTOR;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public class EntityDescriptorRepresentation implements Serializable {
private String entityId;

@Setter
private String groupId;
@Getter
private String idOfOwner;

private String id;

Expand Down Expand Up @@ -110,7 +111,7 @@ public String getId() {
}

public String getGroupId() {
return groupId == null ? Group.ADMIN_GROUP.getResourceId() : groupId;
return idOfOwner == null ? Group.ADMIN_GROUP.getResourceId() : idOfOwner;
}

public List<LogoutEndpointRepresentation> getLogoutEndpoints() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ public interface EntityDescriptorRepository extends JpaRepository<EntityDescript
@Query("select e from EntityDescriptor e")
Stream<EntityDescriptor> findAllStreamByCustomQuery();

Stream<EntityDescriptor> findAllStreamByIdOfOwner(String ownerId);

@Query("select e from EntityDescriptor e, User u join u.roles r " +
"where e.createdBy = u.username and e.serviceEnabled = false and r.name in ('ROLE_USER', 'ROLE_NONE')")
Stream<EntityDescriptor> findAllDisabledAndNotOwnedByAdmin();

Stream<EntityDescriptor> findAllStreamByGroup_resourceId(String resourceId);


/**
* SHIBUI-1740 This is here to aid in migration of systems using the SHIBUI prior to group functionality being added
* @deprecated - this is intended to be removed at some future date and is here only for migration purposes.
*/
@Deprecated
List<EntityDescriptor> findAllByGroupIsNull();
List<EntityDescriptor> findAllByIdOfOwnerIsNull();
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
package edu.internet2.tier.shibboleth.admin.ui.security.controller;

import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
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.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 groovy.util.logging.Slf4j;
import jline.internal.Log;
import static org.springframework.http.HttpStatus.NOT_FOUND;

import java.security.Principal;
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -30,11 +22,14 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;

import java.security.Principal;
import java.util.List;
import java.util.Optional;

import static org.springframework.http.HttpStatus.NOT_FOUND;
import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.OwnershipConflictException;
import edu.internet2.tier.shibboleth.admin.ui.security.model.User;
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 jline.internal.Log;

/**
* Implementation of the REST resource endpoints exposing system users.
Expand All @@ -43,16 +38,10 @@
@RequestMapping("/api/admin/users")
@Slf4j
public class UsersController {
@Autowired
private GroupsRepository groupRepo;

@Autowired
private IGroupService groupService;

private UserRepository userRepository;
private UserService userService;

public UsersController(UserRepository userRepository, RoleRepository roleRepository, UserService userService) {
public UsersController(UserRepository userRepository, UserService userService) {
this.userRepository = userRepository;
this.userService = userService;
}
Expand All @@ -67,12 +56,15 @@ public ResponseEntity<?> deleteOne(@PathVariable String username) {
catch (EntityNotFoundException e) {
throw new HttpClientErrorException(NOT_FOUND, String.format("User with username [%s] not found", username));
}
catch (OwnershipConflictException e) {
throw new HttpClientErrorException(HttpStatus.CONFLICT, e.getMessage());
}
return ResponseEntity.noContent().build();
}

private User findUserOrThrowHttp404(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new HttpClientErrorException(NOT_FOUND, String.format("User with username [%s] not found", username)));
Optional<User> result = userRepository.findByUsername(username);
return result.orElseThrow(() -> new HttpClientErrorException(NOT_FOUND, String.format("User with username [%s] not found", username)));
}

@PreAuthorize("hasRole('ADMIN')")
Expand Down Expand Up @@ -134,6 +126,7 @@ ResponseEntity<?> saveOne(@RequestBody User user) {
@PatchMapping("/{username}")
ResponseEntity<?> updateOne(@PathVariable(value = "username") String username, @RequestBody User user) {
User persistedUser = findUserOrThrowHttp404(username);

if (StringUtils.isNotBlank(user.getFirstName())) {
persistedUser.setFirstName(user.getFirstName());
}
Expand All @@ -147,8 +140,10 @@ ResponseEntity<?> updateOne(@PathVariable(value = "username") String username, @
persistedUser.setPassword(BCrypt.hashpw(user.getPassword(), BCrypt.gensalt()));
}
if (StringUtils.isNotBlank(user.getRole())) {
persistedUser.setRole(user.getRole());
userService.updateUserRole(persistedUser);
if (!user.getRole().equals(persistedUser.getRole())) {
persistedUser.setRole(user.getRole());
userService.updateUserRole(persistedUser);
}
}
persistedUser.setGroupId(user.getGroupId());
User savedUser = userService.save(persistedUser);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.security.exception;

public class OwnershipConflictException extends Exception {
public OwnershipConflictException(String message) {
super(message);
}
}
Loading

0 comments on commit 154cea4

Please sign in to comment.