package org.gbif.registry.ws.resources;

import org.gbif.api.model.common.GbifUser;
import org.gbif.api.service.common.IdentityService;
import org.gbif.api.service.common.LoggedUser;
import org.gbif.api.service.common.LoggedUserWithToken;
import org.gbif.registry.identity.model.UserModelMutationResult;
import org.gbif.registry.ws.model.AuthenticationDataParameters;
import org.gbif.registry.ws.security.jwt.JwtIssuanceService;
import org.gbif.ws.security.GbifAuthentication;
import org.gbif.ws.security.GbifUserPrincipal;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
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.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import static org.gbif.registry.ws.security.SecurityContextCheck.ensureNotGbifScheme;
import static org.gbif.registry.ws.security.SecurityContextCheck.ensureUserSetInSecurityContext;
import static org.gbif.registry.ws.security.UserRoles.USER_ROLE;

@RequestMapping("/user")
@RestController
public class UserResource {

  private final IdentityService identityService;
  private final JwtIssuanceService jwtIssuanceService;

  public UserResource(IdentityService identityService, JwtIssuanceService jwtIssuanceService) {
    this.identityService = identityService;
    this.jwtIssuanceService = jwtIssuanceService;
  }

  /**
   * Check the credentials of a user using Basic Authentication and return a {@link LoggedUser} if successful.
   *
   * @return the user as {@link LoggedUser}
   */
  @RequestMapping(value = "/login", method = RequestMethod.GET)
  public ResponseEntity<LoggedUserWithToken> loginGet() {
    // the user shall be authenticated using basic auth. scheme only.
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    ensureNotGbifScheme(authentication);
    ensureUserSetInSecurityContext(authentication);

    return login(((GbifUserPrincipal) authentication.getPrincipal()).getUsername());
  }

  @RequestMapping(value = "/login", method = RequestMethod.POST)
  public ResponseEntity<LoggedUserWithToken> loginPost() {
    // the user shall be authenticated using basic auth scheme only.
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    ensureNotGbifScheme(authentication);
    ensureUserSetInSecurityContext(authentication);

    return login(((GbifUserPrincipal) authentication.getPrincipal()).getUsername());
  }

  // only to use in login since it updates the last login
  private ResponseEntity<LoggedUserWithToken> login(String username) {
    // get the user
    final GbifUser user = identityService.get(username);

    if (user == null) {
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
    }

    identityService.updateLastLogin(user.getKey());

    final String token = jwtIssuanceService.generateJwt(user.getUserName());

    // build response
    LoggedUserWithToken response = LoggedUserWithToken.from(user, token,
        identityService.listEditorRights(user.getUserName()));

    return ResponseEntity.ok()
        .cacheControl(CacheControl.noCache().cachePrivate())
        .body(response);
  }

  @PostMapping("/whoami")
  public ResponseEntity<LoggedUserWithToken> whoAmI() {
    // the user shall be authenticated using basic auth scheme
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    ensureNotGbifScheme(authentication);
    ensureUserSetInSecurityContext(authentication);

    // get the user
    final GbifAuthentication gbifAuthentication = (GbifAuthentication) authentication;
    final GbifUser user = identityService.get(gbifAuthentication.getPrincipal().getUsername());

    if (user == null) {
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
    }

    return ResponseEntity.ok()
        .cacheControl(CacheControl.noCache().cachePrivate())
        .body(LoggedUserWithToken.from(user, null, identityService.listEditorRights(user.getUserName())));
  }

  /**
   * Allows a user to change its own password.
   */
  @Secured({USER_ROLE})
  @RequestMapping(value = "/changePassword", method = RequestMethod.PUT)
  public ResponseEntity changePassword(@RequestBody AuthenticationDataParameters authenticationDataParameters) {
    // the user shall be authenticated using basic auth scheme
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    ensureNotGbifScheme(authentication);
    ensureUserSetInSecurityContext(authentication);

    final String identifier = ((GbifAuthentication) authentication).getPrincipal().getUsername();
    final GbifUser user = identityService.get(identifier);
    if (user != null) {
      UserModelMutationResult updatePasswordMutationResult =
          identityService.updatePassword(user.getKey(), authenticationDataParameters.getPassword());
      if (updatePasswordMutationResult.containsError()) {
        return ResponseEntity.unprocessableEntity().body(updatePasswordMutationResult);
      }
    }
    return ResponseEntity.noContent().build();
  }
}

@@ -894,19 +894,137 @@
 otation.
-Pos
+GetMapping;%0Aimport org.springframework.web.bind.annotation.PostMapping;%0Aimport org.springframework.web.bind.annotation.Pu
 tMapping
@@ -1152,70 +1152,8 @@
 ng;%0A
-import org.springframework.web.bind.annotation.RequestMethod;%0A
 impo
@@ -1478,17 +1478,16 @@
 apping(%22
-/
 user%22)%0A@
@@ -2001,30 +2001,26 @@
 r%7D%0A   */%0A  @
-Reques
+Ge
 tMapping(val
@@ -2020,52 +2020,15 @@
 ing(
-value = %22/login%22, method = RequestMethod.GET
+%22login%22
 )%0A  
@@ -2434,29 +2434,26 @@
 ));%0A  %7D%0A%0A  @
-Reque
+Po
 stMapping(va
@@ -2454,53 +2454,15 @@
 ing(
-value = %22/login%22, method = RequestMethod.POST
+%22login%22
 )%0A  
@@ -2526,347 +2526,24 @@
     
-// the user shall be authenticated using basic auth scheme only.%0A    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();%0A    ensureNotGbifScheme(authentication);%0A    ensureUserSetInSecurityContext(authentication);%0A%0A    return login(((GbifUserPrincipal) authentication.getPrincipal()).getUsername()
+return loginGet(
 );%0A 
@@ -3080,26 +3080,24 @@
 oken,%0A      
-  
 identityServ
@@ -3163,34 +3163,32 @@
 onseEntity.ok()%0A
-  
       .cacheCont
@@ -3232,26 +3232,24 @@
 te())%0A      
-  
 .body(respon
@@ -3274,17 +3274,16 @@
 apping(%22
-/
 whoami%22)
@@ -3930,26 +3930,24 @@
 .ok()%0A      
-  
 .cacheContro
@@ -3983,26 +3983,24 @@
 ePrivate())%0A
-  
       .body(
@@ -4183,22 +4183,18 @@
 LE%7D)%0A  @
-Reques
+Pu
 tMapping
@@ -4198,18 +4198,9 @@
 ing(
-value = %22/
+%22
 chan
@@ -4214,36 +4214,8 @@
 ord%22
-, method = RequestMethod.PUT
 )%0A  
@@ -4822,18 +4822,16 @@
 esult =%0A
-  
         

Fix issues in UserResource