Skip to content

Commit

Permalink
SHIBUI-2393
Browse files Browse the repository at this point in the history
initial commit
  • Loading branch information
chasegawa committed Nov 11, 2022
1 parent c2062a6 commit c4dd0a7
Show file tree
Hide file tree
Showing 15 changed files with 1,133 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.UserUpdatedEntityListener;
import edu.internet2.tier.shibboleth.admin.ui.security.permission.IShibUiPermissionEvaluator;
import edu.internet2.tier.shibboleth.admin.ui.security.permission.ShibUiPermissionDelegate;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.DynamicRegistrationInfoRepository;
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;
Expand All @@ -21,12 +22,14 @@
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.DynamicRegistrationService;
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;
import edu.internet2.tier.shibboleth.admin.ui.service.FileCheckingFileWritingService;
import edu.internet2.tier.shibboleth.admin.ui.service.FileWritingService;
import edu.internet2.tier.shibboleth.admin.ui.service.FilterTargetService;
import edu.internet2.tier.shibboleth.admin.ui.service.JPADynamicRegistrationServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterTargetServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService;
Expand Down Expand Up @@ -234,8 +237,13 @@ public UserUpdatedEntityListener userUpdatedEntityListener(OwnershipRepository r
}

@Bean
public IShibUiPermissionEvaluator shibUiPermissionEvaluator(EntityDescriptorRepository entityDescriptorRepository, UserService userService) {
public IShibUiPermissionEvaluator shibUiPermissionEvaluator(EntityDescriptorRepository entityDescriptorRepository, UserService userService, DynamicRegistrationInfoRepository driRepo) {
// TODO: @jj define type to return for Grouper integration
return new ShibUiPermissionDelegate(entityDescriptorRepository, userService);
return new ShibUiPermissionDelegate(driRepo, entityDescriptorRepository, userService);
}

@Bean
public DynamicRegistrationService dynamicRegistrationService(DynamicRegistrationInfoRepository driRepo, OwnershipRepository ownershipRepo, IShibUiPermissionEvaluator permissionEvaluator, UserService userService) {
return new JPADynamicRegistrationServiceImpl(driRepo, ownershipRepo, permissionEvaluator, userService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.DynamicRegistrationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.InvalidPatternMatchException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.service.DynamicRegistrationService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;

@RestController
@RequestMapping("/api")
@Tags(value = {@Tag(name = "oidc")})
public class DynamicRegistrationController {
@Autowired
DynamicRegistrationService dynamicRegistrationService;

@PostMapping("/DynamicRegistration")
@Transactional
public ResponseEntity<?> create(@RequestBody DynamicRegistrationRepresentation dynRegRepresentation) throws ForbiddenException, ObjectIdExistsException, InvalidPatternMatchException {
DynamicRegistrationRepresentation persisted = dynamicRegistrationService.createNew(dynRegRepresentation);
return ResponseEntity.created(getResourceUriFor(persisted.getResourceId())).body(persisted);
}

@GetMapping(value = "/DynamicRegistrations", produces = "application/json")
@Transactional(readOnly = true)
public ResponseEntity<?> getAll() throws ForbiddenException {
return ResponseEntity.ok(dynamicRegistrationService.getAllDynamicRegistrationsBasedOnUserAccess());
}

private static URI getResourceUriFor(String resourceId) {
return ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/api/DynamicRegistration")
.pathSegment(resourceId)
.build()
.toUri();
}


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

public enum ActivatableType {
ENTITY_DESCRIPTOR, METADATA_RESOLVER, FILTER
ENTITY_DESCRIPTOR, METADATA_RESOLVER, FILTER, DYNAMIC_REGISTRATION
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

public interface IApprovable {
String getIdOfOwner();

void removeLastApproval();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.frontend;

import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.GrantType;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@NoArgsConstructor
@Getter
@Setter
public class DynamicRegistrationRepresentation {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");

private String applicationType;
private boolean approved;
private String contacts;
private LocalDateTime createdDate;
private boolean enabled;
private GrantType grantType;
private String idOfOwner;
private String jwks;
private String logoUri;
private LocalDateTime modifiedDate;
private String policyUri;
private String redirectUris;
private String resourceId;
private String responseTypes;
private String scope;
private String subjectType;
private String tokenEndpointAuthMethod;
private String tosUri;
private int version;

public DynamicRegistrationRepresentation(DynamicRegistrationInfo dri) {
applicationType = dri.getApplicationType();
approved = dri.isApproved();
contacts = dri.getContacts();
createdDate = dri.getCreatedDate();
enabled = dri.isEnabled();
grantType = dri.getGrantType();
idOfOwner = dri.getIdOfOwner();
jwks = dri.getJwks();
logoUri = dri.getLogoUri();
modifiedDate = dri.getModifiedDate();
policyUri = dri.getPolicyUri();
redirectUris = dri.getRedirectUris();
resourceId = dri.getResourceId();
responseTypes = dri.getResponseTypes();
scope = dri.getScope();
subjectType = dri.getSubjectType();
tokenEndpointAuthMethod = dri.getTokenEndpointAuthMethod();
tosUri = dri.getTosUri();
version = dri.hashCode();
}

public DynamicRegistrationInfo buildDynamicRegistrationInfo() {
// Approved and enabled shouldn't be handled from here, and owner shouldn't come from the UI, so we ignore all those

DynamicRegistrationInfo dri = new DynamicRegistrationInfo();
dri.setApplicationType(applicationType);
// dri.setApproved(approved);
dri.setContacts(contacts);
// dri.setEnabled(enabled);
dri.setGrantType(grantType);
// dri.setIdOfOwner(idOfOwner);
dri.setJwks(jwks);
dri.setLogoUri(logoUri);
dri.setPolicyUri(policyUri);
dri.setRedirectUris(redirectUris);
dri.setResourceId(resourceId);
dri.setResponseTypes(responseTypes);
dri.setScope(scope);
dri.setSubjectType(subjectType);
dri.setTokenEndpointAuthMethod(tokenEndpointAuthMethod);
dri.setTosUri(tosUri);
return dri;
}

public String getCreatedDate() {
return createdDate != null ? DATE_TIME_FORMATTER.format(createdDate) : null;
}

public String getModifiedDate() {
return modifiedDate != null ? DATE_TIME_FORMATTER.format(modifiedDate) : null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.oidc;

import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable;
import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType;
import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable;
import edu.internet2.tier.shibboleth.admin.ui.domain.IApprovable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.OwnableType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.envers.Audited;

import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Entity
@Data
@Audited
public class DynamicRegistrationInfo extends AbstractAuditable implements Ownable, IActivatable, IApprovable {
private String applicationType;
private boolean approved;
private String contacts;
private boolean enabled;
private GrantType grantType;
private String idOfOwner;
private String jwks;
private String logoUri;
private String policyUri;
private String redirectUris;
private String resourceId;
private String responseTypes;
private String scope;
private String subjectType;
private String tokenEndpointAuthMethod;
private String tosUri;

@ElementCollection(fetch = FetchType.EAGER)
@EqualsAndHashCode.Exclude
private List<String> approvedBy = new ArrayList<>();

@Override
public ActivatableType getActivatableType() {
return ActivatableType.DYNAMIC_REGISTRATION;
}

@Override
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

@Override
public String getObjectId() {
return getResourceId();
}

public String getResourceId() {
if (resourceId == null) {
resourceId = UUID.randomUUID().toString();
}
return resourceId;
}

@Override
public OwnableType getOwnableType() {
return OwnableType.DYNAMIC_REGISTRATION;
}

@Override
public void removeLastApproval() {
if (!approvedBy.isEmpty()) {
approvedBy.remove(approvedBy.size() - 1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.oidc;

public enum GrantType {
authorization_code, implicit, refresh_token
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.security.model;

public enum OwnableType {
USER, ENTITY_DESCRIPTOR, METADATA_PROVIDER
}
USER, ENTITY_DESCRIPTOR, METADATA_PROVIDER, DYNAMIC_REGISTRATION
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

public enum ShibUiPermissibleType {
entityDescriptorProjection // represents EntityDescriptorProjections
entityDescriptorProjection, // represents EntityDescriptorProjections
dynamicRegistrationInfo
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

import edu.internet2.tier.shibboleth.admin.ui.controller.DynamicRegistrationController;
import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable;
import edu.internet2.tier.shibboleth.admin.ui.domain.IApprovable;
import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorProjection;
import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.User;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.DynamicRegistrationInfoRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import lombok.AllArgsConstructor;
Expand All @@ -27,6 +30,8 @@
*/
@AllArgsConstructor
public class ShibUiPermissionDelegate implements IShibUiPermissionEvaluator {
private DynamicRegistrationInfoRepository dynamicRegistrationInfoRepository;

private EntityDescriptorRepository entityDescriptorRepository;

private UserService userService;
Expand All @@ -46,14 +51,30 @@ public Collection getPersistentEntities(Authentication ignored, ShibUiPermissibl
return entityDescriptorRepository.getEntityDescriptorsNeedingEnabling();
case fetch:
if (!hasPermission(ignored, null, PermissionType.fetch)) {
throw new ForbiddenException("User has no access rights to get a list of Metadata Sources");
throw new ForbiddenException("User has no access rights to get a list of : " + shibUiType);
}
return getAllEntityDescriptorProjectionsBasedOnUserAccess();
}
case dynamicRegistrationInfo:
switch (permissionType) {
case fetch:
if (!hasPermission(ignored, null, PermissionType.fetch)) {
throw new ForbiddenException("User has no access rights to get a list of : " + shibUiType);
}
return getAllDynamicRegistrationInfoObjectsBasedOnUserAccess();
}
}
return null;
}

private List<DynamicRegistrationInfo> getAllDynamicRegistrationInfoObjectsBasedOnUserAccess() {
if (userService.currentUserIsAdmin()) {
return dynamicRegistrationInfoRepository.findAll();
} else {
return dynamicRegistrationInfoRepository.findAllByIdOfOwner(userService.getCurrentUser().getGroup().getOwnerId());
}
}

private List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsBasedOnUserAccess() {
if (userService.currentUserIsAdmin()) {
return entityDescriptorRepository.findAllReturnProjections();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package edu.internet2.tier.shibboleth.admin.ui.security.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface DynamicRegistrationInfoRepository extends JpaRepository<DynamicRegistrationInfo, String> {
List<DynamicRegistrationInfo> findAllByIdOfOwner(String idOfOwner);

DynamicRegistrationInfo findByResourceId(String id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.DynamicRegistrationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;

public interface DynamicRegistrationService {
Object getAllDynamicRegistrationsBasedOnUserAccess() throws ForbiddenException;

DynamicRegistrationRepresentation createNew(DynamicRegistrationRepresentation dynRegRepresentation) throws ObjectIdExistsException;
}
Loading

0 comments on commit c4dd0a7

Please sign in to comment.