Skip to content

Commit

Permalink
Merge pull request #40 from companieshouse/IDVA6-1326-patch-endpoint
Browse files Browse the repository at this point in the history
Idva6 1326 patch endpoint
  • Loading branch information
krishna-patel-ch authored Jul 25, 2024
2 parents dfff1cc + c50e653 commit 8a3caad
Show file tree
Hide file tree
Showing 11 changed files with 1,069 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package uk.gov.companieshouse.acsp.manage.users.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import static uk.gov.companieshouse.acsp.manage.users.utils.RequestContextUtil.isOAuth2Request;

import java.util.Objects;
import java.util.Optional;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.companieshouse.acsp.manage.users.exceptions.BadRequestRuntimeException;
import uk.gov.companieshouse.acsp.manage.users.exceptions.NotFoundRuntimeException;
import uk.gov.companieshouse.acsp.manage.users.model.AcspMembersDao;
import uk.gov.companieshouse.acsp.manage.users.model.UserContext;
import uk.gov.companieshouse.acsp.manage.users.service.AcspMembersService;
import uk.gov.companieshouse.acsp.manage.users.utils.StaticPropertyUtil;
import uk.gov.companieshouse.api.accounts.user.model.User;
import uk.gov.companieshouse.api.acsp_manage_users.api.AcspMembershipInterface;
import uk.gov.companieshouse.api.acsp_manage_users.model.AcspMembership;
import uk.gov.companieshouse.api.acsp_manage_users.model.AcspMembership.UserRoleEnum;
import uk.gov.companieshouse.api.acsp_manage_users.model.RequestBodyPatch;
import uk.gov.companieshouse.logging.Logger;
import uk.gov.companieshouse.logging.LoggerFactory;
Expand All @@ -22,6 +28,8 @@ public class AcspMembershipController implements AcspMembershipInterface {

private static final Logger LOG = LoggerFactory.getLogger( StaticPropertyUtil.APPLICATION_NAMESPACE );

private static final String PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN = "Please check the request and try again";

public AcspMembershipController( final AcspMembersService acspMembershipService ) {
this.acspMembershipService = acspMembershipService;
}
Expand All @@ -36,11 +44,70 @@ public ResponseEntity<AcspMembership> getAcspMembershipForAcspAndId( final Strin
return new ResponseEntity<>( membership, HttpStatus.OK );
}

private void throwBadRequestWhenActionIsNotPermittedByOAuth2User( final User requestingUser, final AcspMembersDao membershipIdAssociation, final UserRoleEnum userRole ){
if ( UserRoleEnum.OWNER.equals( userRole ) ){
LOG.error( String.format( "User is not permitted to change Acsp Membership %s's role to owner", membershipIdAssociation.getId() ) );
throw new BadRequestRuntimeException( PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
}

final var requestUserAssociation =
acspMembershipService.fetchActiveAcspMembership( requestingUser.getUserId(), membershipIdAssociation.getAcspNumber() )
.orElseThrow( () -> {
LOG.error( String.format( "Could not find %s's Acsp Membership at Acsp %s", requestingUser.getUserId(), membershipIdAssociation.getAcspNumber() ) );
return new NotFoundRuntimeException( StaticPropertyUtil.APPLICATION_NAMESPACE, PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
} );

if ( UserRoleEnum.STANDARD.getValue().equals( requestUserAssociation.getUserRole() ) ){
LOG.error( "User is not permitted to perform this action because their role is 'standard'" );
throw new BadRequestRuntimeException( PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
}

if ( UserRoleEnum.ADMIN.getValue().equals( requestUserAssociation.getUserRole() ) && UserRoleEnum.OWNER.getValue().equals( membershipIdAssociation.getUserRole() ) ){
LOG.error( "User is not permitted to perform this action because their role is 'admin' and the target user's role is 'owner'" );
throw new BadRequestRuntimeException( PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
}
}

@Override
public ResponseEntity<Void> updateAcspMembershipForAcspAndId(@NotNull String s,
@Pattern(regexp = "^[a-zA-Z0-9]*$") String s1,
@Valid RequestBodyPatch requestBodyPatch) {
return null;
public ResponseEntity<Void> updateAcspMembershipForAcspAndId( final String xRequestId, final String membershipId, final RequestBodyPatch requestBody ) {

LOG.infoContext( xRequestId, String.format( "Attempting to update Acsp Membership %s", membershipId ), null );

if ( Objects.isNull( requestBody ) || ( Objects.isNull( requestBody.getUserStatus() ) && Objects.isNull( requestBody.getUserRole() ) ) ){
LOG.error( "Request body is empty" );
throw new BadRequestRuntimeException( PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
}
final var userStatus = requestBody.getUserStatus();

final var userRole =
Optional.ofNullable( requestBody.getUserRole() )
.map( RequestBodyPatch.UserRoleEnum::getValue )
.map( UserRoleEnum::fromValue )
.orElse( null );

final var membershipIdAssociation =
acspMembershipService.fetchMembershipDao( membershipId )
.orElseThrow( () -> {
LOG.error( String.format( "Could not find Acsp Membership %s", membershipId ) );
return new NotFoundRuntimeException( StaticPropertyUtil.APPLICATION_NAMESPACE, PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
} );

if ( UserRoleEnum.OWNER.getValue().equals( membershipIdAssociation.getUserRole() ) && acspMembershipService.fetchNumberOfActiveOwners( membershipIdAssociation.getAcspNumber() ) <= 1 ){
LOG.error( String.format( "Acsp Membership %s is the last owner", membershipId ) );
throw new BadRequestRuntimeException( PLEASE_CHECK_THE_REQUEST_AND_TRY_AGAIN );
}

final var requestingUser = UserContext.getLoggedUser();
if ( isOAuth2Request() ){
throwBadRequestWhenActionIsNotPermittedByOAuth2User( requestingUser, membershipIdAssociation, userRole );
}

final var requestingUserId = Optional.ofNullable( requestingUser ).map( User::getUserId ).orElse( null );
acspMembershipService.updateMembership( membershipId, userStatus, userRole, requestingUserId );

LOG.infoContext( xRequestId, String.format( "Successfully updated Acsp Membership %s", membershipId ), null );

return new ResponseEntity<>( HttpStatus.OK );
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package uk.gov.companieshouse.acsp.manage.users.interceptor;

import static uk.gov.companieshouse.acsp.manage.users.utils.RequestContextUtil.isOAuth2Request;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import uk.gov.companieshouse.acsp.manage.users.model.UserContext;
import uk.gov.companieshouse.acsp.manage.users.service.UsersService;
import uk.gov.companieshouse.acsp.manage.users.utils.StaticPropertyUtil;
import uk.gov.companieshouse.api.interceptor.InternalUserInterceptor;
Expand Down Expand Up @@ -43,4 +47,11 @@ public boolean preHandle( HttpServletRequest request, HttpServletResponse respon
return false;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if ( isOAuth2Request() ) {
UserContext.clear();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
Expand Down Expand Up @@ -31,4 +33,14 @@ Page<AcspMembersDao> findAllByAcspNumberAndUserRole(

@Query(value = "{ 'user_id': ?0, 'status': 'active' }")
List<AcspMembersDao> fetchActiveAcspMembersByUserId(final String userId);

@Query( value = "{ 'acsp_number': ?0, 'user_role': 'owner', 'status': 'active' }", count = true )
int fetchNumberOfActiveOwners( final String acspNumber );

@Query( "{ 'user_id': ?0, 'acsp_number': ?1, 'status': 'active' }" )
Optional<AcspMembersDao> fetchActiveAcspMembership( final String userId, final String acspNumber );

@Query( "{ '_id': ?0 }" )
int updateAcspMembership( final String acspMembershipId, final Update update );

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

import java.util.List;
import java.util.Objects;
import static uk.gov.companieshouse.GenerateEtagUtil.generateEtag;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import uk.gov.companieshouse.acsp.manage.users.exceptions.InternalServerErrorRuntimeException;
import uk.gov.companieshouse.acsp.manage.users.mapper.AcspMembershipListMapper;
import uk.gov.companieshouse.acsp.manage.users.mapper.AcspMembershipMapper;
import uk.gov.companieshouse.acsp.manage.users.mapper.AcspMembershipsListMapper;
Expand All @@ -17,7 +23,9 @@
import uk.gov.companieshouse.acsp.manage.users.utils.StaticPropertyUtil;
import uk.gov.companieshouse.api.accounts.user.model.User;
import uk.gov.companieshouse.api.acsp_manage_users.model.AcspMembership;
import uk.gov.companieshouse.api.acsp_manage_users.model.AcspMembership.UserRoleEnum;
import uk.gov.companieshouse.api.acsp_manage_users.model.AcspMembershipsList;
import uk.gov.companieshouse.api.acsp_manage_users.model.RequestBodyPatch.UserStatusEnum;
import uk.gov.companieshouse.logging.Logger;
import uk.gov.companieshouse.logging.LoggerFactory;

Expand Down Expand Up @@ -93,9 +101,50 @@ public AcspMembershipsList fetchAcspMemberships(final User user, final boolean i
return acspMembershipsList;
}

@Transactional( readOnly = true )
public Optional<AcspMembersDao> fetchMembershipDao( final String membershipId ){
return acspMembersRepository.findById( membershipId );
}

@Transactional( readOnly = true )
public Optional<AcspMembership> fetchMembership( final String membershipId ){
return acspMembersRepository.findById( membershipId ).map( acspMembershipMapper::daoToDto );
return fetchMembershipDao( membershipId ).map( acspMembershipMapper::daoToDto );
}

@Transactional( readOnly = true )
public int fetchNumberOfActiveOwners( final String acspNumber ){
return acspMembersRepository.fetchNumberOfActiveOwners( acspNumber );
}

@Transactional( readOnly = true )
public Optional<AcspMembersDao> fetchActiveAcspMembership( final String userId, final String acspNumber ){
return acspMembersRepository.fetchActiveAcspMembership( userId, acspNumber );
}

@Transactional
public void updateMembership( final String membershipId, final UserStatusEnum userStatus, final UserRoleEnum userRole, final String requestingUserId ){
if ( Objects.isNull( membershipId ) ){
throw new IllegalArgumentException( "membershipId cannot be null" );
}

final var update = new Update();
update.set( "etag", generateEtag() );

if ( Objects.nonNull( userRole ) ){
update.set( "user_role", userRole.getValue() );
}

if ( Objects.nonNull( userStatus ) ){
update.set( "status", userStatus.getValue() );
update.set( "removed_by", requestingUserId );
update.set( "removed_at", LocalDateTime.now() );
}

final var numRecordsUpdated = acspMembersRepository.updateAcspMembership( membershipId, update );
if ( numRecordsUpdated == 0 ){
LOG.error( String.format( "Failed to update Acsp Membership %s", membershipId ) );
throw new InternalServerErrorRuntimeException( String.format( "Failed to update Acsp Membership %s", membershipId ) );
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@

public class RequestContextUtil {

public static String getEricIdentityType(){
private static final String OAUTH2_REQUEST_TYPE = "oauth2";

private static String getEricIdentityType(){
final var requestAttributes = RequestContextHolder.getRequestAttributes();
final var servletRequestAttributes = ( (ServletRequestAttributes) requestAttributes );
final var httpServletRequest = servletRequestAttributes.getRequest();
return httpServletRequest.getHeader( ERIC_IDENTITY_TYPE );
}

public static boolean isOAuth2Request(){
return getEricIdentityType().equals( OAUTH2_REQUEST_TYPE );
}

}
Loading

0 comments on commit 8a3caad

Please sign in to comment.