diff --git a/src/main/java/bio/overture/ego/controller/VisaController.java b/src/main/java/bio/overture/ego/controller/VisaController.java index 647defcc..03997a97 100644 --- a/src/main/java/bio/overture/ego/controller/VisaController.java +++ b/src/main/java/bio/overture/ego/controller/VisaController.java @@ -12,6 +12,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import java.util.List; import java.util.UUID; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @@ -29,6 +31,8 @@ public class VisaController { /** Dependencies */ private final VisaService visaService; + private final VisaPermissionService visaPermissionService; + private final UserPermissionService userPermissionService; private final GroupPermissionService groupPermissionService; private final ApplicationPermissionService applicationPermissionService; @@ -36,10 +40,12 @@ public class VisaController { @Autowired public VisaController( @NonNull VisaService visaService, + @NotNull VisaPermissionService visaPermissionService, @NonNull UserPermissionService userPermissionService, @NonNull GroupPermissionService groupPermissionService, @NonNull ApplicationPermissionService applicationPermissionService) { this.visaService = visaService; + this.visaPermissionService = visaPermissionService; this.groupPermissionService = groupPermissionService; this.userPermissionService = userPermissionService; this.applicationPermissionService = applicationPermissionService; @@ -61,6 +67,10 @@ public VisaController( return visaService.getById(id); } + /* + * This method is used to list all visas + * @return visas List + */ @AdminScoped @RequestMapping(method = GET, value = "") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "All Visas")}) @@ -72,6 +82,11 @@ public VisaController( return new PageDTO<>(visaService.listVisa(pageable)); } + /* + * This method is used to create visa using visa create request + * @param visaRequest VisaRequest + * @return Visa visa + */ @AdminScoped @RequestMapping(method = POST, value = "") @ApiResponses( @@ -85,6 +100,12 @@ public VisaController( return visaService.create(visaRequest); } + /* + * This method is used to update visa using visa id and update request + * @param visaId UUID + * @param visaRequest VisaRequest + * @return Visa visa + */ @AdminScoped @RequestMapping(method = PUT, value = "/{id}") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Update Visa")}) @@ -96,6 +117,10 @@ public VisaController( return visaService.partialUpdate(id, visaRequest); } + /* + * This method is used to delete visa using visa id + * @param visaId UUID + */ @AdminScoped @RequestMapping(method = DELETE, value = "/{id}") @ResponseStatus(value = HttpStatus.OK) @@ -105,4 +130,71 @@ public void deleteVisa( @PathVariable(value = "id", required = true) UUID id) { visaService.delete(id); } + + /* + * This method is used to fetch visa permissions using visa id + * @param visaId UUID + * @return visaPermissions List + */ + @AdminScoped + @RequestMapping(method = GET, value = "/permissions/visaId/{id}") + @ApiResponses( + value = {@ApiResponse(responseCode = "200", description = "Get VisaPermissions by visaId")}) + @JsonView(Views.REST.class) + public @ResponseBody List getPermissionsByVisaId( + @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "id", required = true) UUID id) { + return visaPermissionService.getPermissionsByVisaId(id); + } + + /* + * This method is used to fetch visa permissions using policy id + * @param policyId UUID + * @return visaPermissions List + */ + @AdminScoped + @RequestMapping(method = GET, value = "/permissions/policyId/{id}") + @ApiResponses( + value = {@ApiResponse(responseCode = "200", description = "Get VisaPermissions by policyId")}) + @JsonView(Views.REST.class) + public @ResponseBody List getPermissionsByPolicyId( + @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "id", required = true) UUID id) { + return visaPermissionService.getPermissionsByPolicyId(id); + } + + /* + * This method is used to create/update visa permissions + * @param visaPermissionRequest VisaPermissionRequest + * @return visaPermission VisaPermission + */ + @AdminScoped + @RequestMapping(method = POST, value = "/permissions") + @ApiResponses( + value = {@ApiResponse(responseCode = "200", description = "Create or Update VisaPermission")}) + @JsonView(Views.REST.class) + public @ResponseBody VisaPermission createOrUpdatePermissions( + @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) + final String authorization, + @RequestBody(required = true) VisaPermissionRequest visaPermissionRequest) { + return visaPermissionService.createOrUpdatePermissions(visaPermissionRequest); + } + + /* + * This method is used to delete/remove visa permissions + * @param visaPermissionRequest VisaPermissionRequest + */ + @AdminScoped + @RequestMapping(method = DELETE, value = "/permissions/{policyId}/{visaId}") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Remove VisaPermission")}) + @JsonView(Views.REST.class) + public @ResponseBody void removePermissions( + @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "policyId", required = true) UUID policyId, + @PathVariable(value = "visaId", required = true) UUID visaId) { + visaPermissionService.removePermission(policyId, visaId); + } } diff --git a/src/main/java/bio/overture/ego/model/dto/VisaPermissionRequest.java b/src/main/java/bio/overture/ego/model/dto/VisaPermissionRequest.java new file mode 100644 index 00000000..770c1e42 --- /dev/null +++ b/src/main/java/bio/overture/ego/model/dto/VisaPermissionRequest.java @@ -0,0 +1,18 @@ +package bio.overture.ego.model.dto; + +import bio.overture.ego.model.enums.AccessLevel; +import java.util.UUID; +import lombok.*; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class VisaPermissionRequest { + + private UUID policyId; + + private UUID visaId; + + private AccessLevel accessLevel; +} diff --git a/src/main/java/bio/overture/ego/model/entity/Visa.java b/src/main/java/bio/overture/ego/model/entity/Visa.java index f44a20af..ce879437 100644 --- a/src/main/java/bio/overture/ego/model/entity/Visa.java +++ b/src/main/java/bio/overture/ego/model/entity/Visa.java @@ -64,7 +64,7 @@ public class Visa implements Identifiable { private String by; @JsonIgnore - @ManyToMany(mappedBy = "visaId", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @OneToMany(mappedBy = JavaFields.VISA, cascade = CascadeType.ALL, fetch = FetchType.LAZY) @Builder.Default private Set visaPermissions = newHashSet(); } diff --git a/src/main/java/bio/overture/ego/model/entity/VisaPermission.java b/src/main/java/bio/overture/ego/model/entity/VisaPermission.java index 67223b63..ebac950a 100644 --- a/src/main/java/bio/overture/ego/model/entity/VisaPermission.java +++ b/src/main/java/bio/overture/ego/model/entity/VisaPermission.java @@ -1,20 +1,13 @@ package bio.overture.ego.model.entity; - -import bio.overture.ego.model.enums.AccessLevel; +import bio.overture.ego.model.enums.JavaFields; import bio.overture.ego.model.enums.SqlFields; import bio.overture.ego.model.enums.Tables; import bio.overture.ego.view.Views; import com.fasterxml.jackson.annotation.JsonView; -import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType; import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import java.util.Collection; -import java.util.UUID; import lombok.*; import lombok.experimental.FieldNameConstants; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Type; @Entity @Table(name = Tables.ACLVISAPERMISSION) @@ -25,32 +18,20 @@ @JsonView(Views.REST.class) @ToString(callSuper = true) @FieldNameConstants +@EqualsAndHashCode( + callSuper = true, + of = {"id"}) +@NamedEntityGraph( + name = "visa-permission-entity-with-relationships", + attributeNodes = { + @NamedAttributeNode(value = JavaFields.POLICY), + @NamedAttributeNode(value = JavaFields.VISA) + }) public class VisaPermission extends AbstractPermission { - @Id - @Column(name = SqlFields.ID, updatable = false, nullable = false) - @GenericGenerator(name = "aclp_uuid", strategy = "org.hibernate.id.UUIDGenerator") - @GeneratedValue(generator = "aclp_uuid") - private UUID id; - - @JoinColumn(name = SqlFields.ID, nullable = false) - private UUID entity; - - @JoinColumn(name = SqlFields.ID, nullable = false) - private UUID visaId; - - @NotNull - @Column(name = SqlFields.MASK, nullable = false) - @Enumerated(EnumType.STRING) - @Type(value = PostgreSQLEnumType.class) - private AccessLevel mask; - - @ManyToMany - @JoinTable( - name = "ga4ghvisa", - joinColumns = @JoinColumn(name = "aclp_id", referencedColumnName = "visaId"), - inverseJoinColumns = @JoinColumn(name = "visa_id", referencedColumnName = "id")) - private Collection visas; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = SqlFields.VISA_ID, nullable = false) + private Visa visa; @Override public Visa getOwner() { diff --git a/src/main/java/bio/overture/ego/model/enums/JavaFields.java b/src/main/java/bio/overture/ego/model/enums/JavaFields.java index aecbeaf1..5592b257 100644 --- a/src/main/java/bio/overture/ego/model/enums/JavaFields.java +++ b/src/main/java/bio/overture/ego/model/enums/JavaFields.java @@ -71,6 +71,7 @@ public class JavaFields { public static final String VALUE = "value"; public static final String BY = "by"; - public static final String VISAPERMISSION = "ACLVISAPERMISSION"; + public static final String GA4GHVISA = "ga4ghvisa"; + public static final String VISA = "visa"; } diff --git a/src/main/java/bio/overture/ego/model/enums/SqlFields.java b/src/main/java/bio/overture/ego/model/enums/SqlFields.java index 488b3c8c..dfdf0a5f 100644 --- a/src/main/java/bio/overture/ego/model/enums/SqlFields.java +++ b/src/main/java/bio/overture/ego/model/enums/SqlFields.java @@ -40,12 +40,6 @@ public class SqlFields { public static final String ERRORREDIRECTURI = "errorredirecturi"; public static final String SOURCE = "source"; public static final String VALUE = "value"; - public static final String BY = "by"; - - public static final String ENTITY = "entity"; - - public static final String VISAID = "visaId"; - - public static final String MASK = "mask"; + public static final String VISA_ID = "visa_id"; } diff --git a/src/main/java/bio/overture/ego/model/enums/Tables.java b/src/main/java/bio/overture/ego/model/enums/Tables.java index 861fe797..e4198aaf 100644 --- a/src/main/java/bio/overture/ego/model/enums/Tables.java +++ b/src/main/java/bio/overture/ego/model/enums/Tables.java @@ -22,8 +22,6 @@ public class Tables { public static final String APPLICATION_PERMISSION = "applicationpermission"; public static final String DEFAULTPROVIDERTRIPWIRE = "defaultprovidertripwire"; public static final String INITTRIPWIRE = "inittripwire"; - public static final String GA4GHVISA = "ga4ghvisa"; - public static final String ACLVISAPERMISSION = "ACLVISAPERMISSION"; } diff --git a/src/main/java/bio/overture/ego/repository/VisaPermissionRepository.java b/src/main/java/bio/overture/ego/repository/VisaPermissionRepository.java new file mode 100644 index 00000000..abee391d --- /dev/null +++ b/src/main/java/bio/overture/ego/repository/VisaPermissionRepository.java @@ -0,0 +1,28 @@ +package bio.overture.ego.repository; + +import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.FETCH; + +import bio.overture.ego.model.entity.VisaPermission; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.springframework.data.jpa.repository.EntityGraph; + +public interface VisaPermissionRepository extends NamedRepository { + @Override + @Deprecated + default Optional findByName(String name) { + return null; + } + + List findAll(); + + @EntityGraph(value = "visa-permission-entity-with-relationships", type = FETCH) + List findByVisa_Id(UUID visa_id); + + @EntityGraph(value = "visa-permission-entity-with-relationships", type = FETCH) + List findByPolicy_Id(UUID policy_id); + + @EntityGraph(value = "visa-permission-entity-with-relationships", type = FETCH) + List findByPolicyIdAndVisaId(UUID policy_id, UUID visa_id); +} diff --git a/src/main/java/bio/overture/ego/service/VisaPermissionService.java b/src/main/java/bio/overture/ego/service/VisaPermissionService.java index 65440038..6057ce5b 100644 --- a/src/main/java/bio/overture/ego/service/VisaPermissionService.java +++ b/src/main/java/bio/overture/ego/service/VisaPermissionService.java @@ -1,7 +1,23 @@ package bio.overture.ego.service; -import bio.overture.ego.model.entity.*; +import static java.lang.String.format; +import static org.mapstruct.factory.Mappers.getMapper; + +import bio.overture.ego.event.token.ApiKeyEventsPublisher; +import bio.overture.ego.model.dto.VisaPermissionRequest; +import bio.overture.ego.model.entity.VisaPermission; +import bio.overture.ego.model.exceptions.NotFoundException; +import bio.overture.ego.repository.VisaPermissionRepository; +import jakarta.validation.constraints.NotNull; +import java.util.List; +import java.util.UUID; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,8 +25,92 @@ @Slf4j @Service @Transactional -public class VisaPermissionService { +public class VisaPermissionService extends AbstractNamedService { /** Dependencies */ @Autowired private VisaService visaService; + + @Autowired private PolicyService policyService; + + @Autowired private VisaPermissionRepository visaPermissionRepository; + private final ApiKeyEventsPublisher apiKeyEventsPublisher; + + private static final VisaPermissionService.VisaPermissionConverter VISA_PERMISSION_CONVERTER = + getMapper(VisaPermissionService.VisaPermissionConverter.class); + + @Autowired + public VisaPermissionService( + @NonNull VisaPermissionRepository visaPermissionRepository, + @NonNull VisaService visaService, + @NonNull ApiKeyEventsPublisher apiKeyEventsPublisher) { + super(VisaPermission.class, visaPermissionRepository); + this.visaPermissionRepository = visaPermissionRepository; + this.visaService = visaService; + this.apiKeyEventsPublisher = apiKeyEventsPublisher; + } + + public List getPermissionsByVisaId(@NonNull UUID visaId) { + val result = (List) visaPermissionRepository.findByVisa_Id(visaId); + if (result.isEmpty()) { + throw new NotFoundException(format("No VisaPermissions exists with visaId '%s'", visaId)); + } + return result; + } + + public List getPermissionsByPolicyId(@NonNull UUID policyId) { + val result = (List) visaPermissionRepository.findByPolicy_Id(policyId); + if (result.isEmpty()) { + throw new NotFoundException(format("No VisaPermissions exists with policyId '%s'", policyId)); + } + return result; + } + + public VisaPermission createOrUpdatePermissions( + @NonNull VisaPermissionRequest visaPermissionRequest) { + VisaPermission visaPermission = null; + List visaPermissionEntities = + visaPermissionRepository.findByPolicyIdAndVisaId( + visaPermissionRequest.getPolicyId(), visaPermissionRequest.getVisaId()); + if (visaPermissionEntities.isEmpty()) { + visaPermission = new VisaPermission(); + visaPermission.setVisa(visaService.getById(visaPermissionRequest.getVisaId())); + visaPermission.setPolicy(policyService.getById(visaPermissionRequest.getPolicyId())); + visaPermission.setAccessLevel(visaPermissionRequest.getAccessLevel()); + return visaPermissionRepository.save(visaPermission); + } else { + VISA_PERMISSION_CONVERTER.updateVisaPermission( + visaPermissionRequest, visaPermissionEntities.get(0)); + return visaPermissionRepository.save(visaPermissionEntities.get(0)); + } + } + + public void removePermission(@NonNull UUID policyId, @NotNull UUID visaId) { + VisaPermission visaPermission = null; + List visaPermissionEntities = + visaPermissionRepository.findByPolicyIdAndVisaId(policyId, visaId); + if (!visaPermissionEntities.isEmpty()) { + visaPermissionRepository.deleteById(visaPermissionEntities.get(0).getId()); + } else { + throw new NotFoundException( + format("No VisaPermissions exists with policyId '%s' and visaId '%s'", policyId, visaId)); + } + } + + @Override + public VisaPermission getById(@NonNull UUID uuid) { + return super.getById(uuid); + } + + @Override + public VisaPermission getWithRelationships(UUID uuid) { + return null; + } + + @Mapper( + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.WARN) + public abstract static class VisaPermissionConverter { + public abstract void updateVisaPermission( + VisaPermissionRequest visaPermissionRequest, @MappingTarget VisaPermission visaPermission); + } } diff --git a/src/main/resources/flyway/sql/V1_23__add_visa_pcl.sql b/src/main/resources/flyway/sql/V1_23__add_visa_pcl.sql index 35557eef..0d2e780c 100644 --- a/src/main/resources/flyway/sql/V1_23__add_visa_pcl.sql +++ b/src/main/resources/flyway/sql/V1_23__add_visa_pcl.sql @@ -1,8 +1,8 @@ CREATE TABLE ACLVISAPERMISSION ( id UUID PRIMARY KEY, - entity UUID, - visaId UUID, - mask ACLMASK NOT NULL, - FOREIGN KEY (entity) REFERENCES POLICY(id), - FOREIGN KEY (visaId) REFERENCES GA4GHVISA(id) + policy_id UUID, + visa_id UUID, + access_level ACLMASK NOT NULL, + FOREIGN KEY (policy_id) REFERENCES POLICY(id), + FOREIGN KEY (visa_id) REFERENCES GA4GHVISA(id) );