<commit_before>package org.gbif.registry.ws.resources;

import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import org.gbif.api.model.common.GbifUser;
import org.gbif.api.model.common.paging.Pageable;
import org.gbif.api.model.common.paging.PagingRequest;
import org.gbif.api.model.common.paging.PagingResponse;
import org.gbif.api.model.registry.ConfirmationKeyParameter;
import org.gbif.api.service.common.IdentityService;
import org.gbif.api.service.common.LoggedUser;
import org.gbif.api.vocabulary.UserRole;
import org.gbif.registry.identity.model.UserModelMutationResult;
import org.gbif.registry.ws.UpdatePasswordException;
import org.gbif.registry.ws.model.AuthenticationDataParameters;
import org.gbif.registry.ws.model.UserAdminView;
import org.gbif.registry.ws.model.UserCreation;
import org.gbif.registry.ws.model.UserUpdate;
import org.gbif.registry.ws.security.SecurityContextCheck;
import org.gbif.registry.ws.security.UserUpdateRulesManager;
import org.gbif.utils.AnnotationUtils;
import org.gbif.ws.security.AppkeysConfiguration;
import org.gbif.ws.security.GbifAuthentication;
import org.gbif.ws.server.filter.AppIdentityFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import static org.gbif.registry.ws.security.UserRoles.ADMIN_ROLE;
import static org.gbif.registry.ws.security.UserRoles.APP_ROLE;
import static org.gbif.registry.ws.security.UserRoles.USER_ROLE;

/**
 * The "/admin/user" resource represents the "endpoints" related to user management. This means the resource
 * it expected to be called by another application (mostly the Registry Console and the portal backend).
 * <p>
 * Design and implementation decisions:
 * - This resource contains mostly the routing to the business logic ({@link IdentityService}) including
 * authorizations. This resource does NOT implement the service but aggregates it (by Dependency Injection).
 * - Methods can return {@link ResponseEntity} instead of object to minimize usage of exceptions and provide
 * better control over the HTTP code returned. This also allows to return an entity in case
 * of errors (e.g. {@link UserModelMutationResult}
 * - keys (user id) are not considered public, therefore the username is used as key
 * - In order to strictly control the data that is exposed this class uses "view models" (e.g. {@link UserAdminView})
 * <p>
 * Please note there is 3 possible ways to be authenticated:
 * - HTTP Basic authentication
 * - User impersonation using appKey. ALL applications with a valid appKey can impersonate a user.
 * - Application itself (APP_ROLE). All applications with a valid appKey that is also present in the appKey whitelist.
 * See {@link AppIdentityFilter}.
 */
@RestController
@RequestMapping("/admin/user")
public class UserManagementResource {

  //filters roles that are deprecated
  private static final List<UserRole> USER_ROLES = Arrays.stream(UserRole.values()).filter(r ->
      !AnnotationUtils.isFieldDeprecated(UserRole.class, r.name())).collect(Collectors.toList());

  private final IdentityService identityService;
  private final List<String> appKeyWhitelist;

  /**
   * {@link UserManagementResource} main constructor.
   */
  public UserManagementResource(IdentityService identityService, AppkeysConfiguration appkeysConfiguration) {
    this.identityService = identityService;
    appKeyWhitelist = appkeysConfiguration.getWhitelist();
  }

  @GetMapping("roles")
  public List<UserRole> listRoles() {
    return USER_ROLES;
  }

  /**
   * GET a {@link UserAdminView} of a user.
   * Mostly for admin console and access by authorized appkey (e.g. portal nodejs backend).
   * Returns the identified user account.
   *
   * @return the {@link UserAdminView} or null
   */
  @Secured({ADMIN_ROLE, APP_ROLE})
  @GetMapping("/{username}")
//  @NullToNotFound
  public UserAdminView getUser(@PathVariable String username) {

    GbifUser user = identityService.get(username);
    if (user == null) {
      return null;
    }
    return new UserAdminView(user, identityService.hasPendingConfirmation(user.getKey()));
  }

  @Secured({ADMIN_ROLE, APP_ROLE})
  @GetMapping("/find")
  public UserAdminView getUserBySystemSetting(@RequestParam Map<String, String> systemSettings) {
    GbifUser user = null;
    Iterator<Map.Entry<String, String>> it = systemSettings.entrySet().iterator();
    if (it.hasNext()) {
      Map.Entry<String, String> paramPair = it.next();
      user = identityService.getBySystemSetting(paramPair.getKey(), paramPair.getValue());
      it.remove();
    }<commit_msg>Remove unused annotation<commit_after>package org.gbif.registry.ws.resources;

import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import org.gbif.api.model.common.GbifUser;
import org.gbif.api.model.common.paging.Pageable;
import org.gbif.api.model.common.paging.PagingRequest;
import org.gbif.api.model.common.paging.PagingResponse;
import org.gbif.api.model.registry.ConfirmationKeyParameter;
import org.gbif.api.service.common.IdentityService;
import org.gbif.api.service.common.LoggedUser;
import org.gbif.api.vocabulary.UserRole;
import org.gbif.registry.identity.model.UserModelMutationResult;
import org.gbif.registry.ws.UpdatePasswordException;
import org.gbif.registry.ws.model.AuthenticationDataParameters;
import org.gbif.registry.ws.model.UserAdminView;
import org.gbif.registry.ws.model.UserCreation;
import org.gbif.registry.ws.model.UserUpdate;
import org.gbif.registry.ws.security.SecurityContextCheck;
import org.gbif.registry.ws.security.UserUpdateRulesManager;
import org.gbif.utils.AnnotationUtils;
import org.gbif.ws.security.AppkeysConfiguration;
import org.gbif.ws.security.GbifAuthentication;
import org.gbif.ws.server.filter.AppIdentityFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import static org.gbif.registry.ws.security.UserRoles.ADMIN_ROLE;
import static org.gbif.registry.ws.security.UserRoles.APP_ROLE;
import static org.gbif.registry.ws.security.UserRoles.USER_ROLE;

/**
 * The "/admin/user" resource represents the "endpoints" related to user management. This means the resource
 * it expected to be called by another application (mostly the Registry Console and the portal backend).
 * <p>
 * Design and implementation decisions:
 * - This resource contains mostly the routing to the business logic ({@link IdentityService}) including
 * authorizations. This resource does NOT implement the service but aggregates it (by Dependency Injection).
 * - Methods can return {@link ResponseEntity} instead of object to minimize usage of exceptions and provide
 * better control over the HTTP code returned. This also allows to return an entity in case
 * of errors (e.g. {@link UserModelMutationResult}
 * - keys (user id) are not considered public, therefore the username is used as key
 * - In order to strictly control the data that is exposed this class uses "view models" (e.g. {@link UserAdminView})
 * <p>
 * Please note there is 3 possible ways to be authenticated:
 * - HTTP Basic authentication
 * - User impersonation using appKey. ALL applications with a valid appKey can impersonate a user.
 * - Application itself (APP_ROLE). All applications with a valid appKey that is also present in the appKey whitelist.
 * See {@link AppIdentityFilter}.
 */
@RestController
@RequestMapping("/admin/user")
public class UserManagementResource {

  //filters roles that are deprecated
  private static final List<UserRole> USER_ROLES = Arrays.stream(UserRole.values()).filter(r ->
      !AnnotationUtils.isFieldDeprecated(UserRole.class, r.name())).collect(Collectors.toList());

  private final IdentityService identityService;
  private final List<String> appKeyWhitelist;

  /**
   * {@link UserManagementResource} main constructor.
   */
  public UserManagementResource(IdentityService identityService, AppkeysConfiguration appkeysConfiguration) {
    this.identityService = identityService;
    appKeyWhitelist = appkeysConfiguration.getWhitelist();
  }

  @GetMapping("roles")
  public List<UserRole> listRoles() {
    return USER_ROLES;
  }

  /**
   * GET a {@link UserAdminView} of a user.
   * Mostly for admin console and access by authorized appkey (e.g. portal nodejs backend).
   * Returns the identified user account.
   *
   * @return the {@link UserAdminView} or null
   */
  @Secured({ADMIN_ROLE, APP_ROLE})
  @GetMapping("/{username}")
  public UserAdminView getUser(@PathVariable String username) {

    GbifUser user = identityService.get(username);
    if (user == null) {
      return null;
    }
    return new UserAdminView(user, identityService.hasPendingConfirmation(user.getKey()));
  }

  @Secured({ADMIN_ROLE, APP_ROLE})
  @GetMapping("/find")
  public UserAdminView getUserBySystemSetting(@RequestParam Map<String, String> systemSettings) {
    GbifUser user = null;
    Iterator<Map.Entry<String, String>> it = systemSettings.entrySet().iterator();
    if (it.hasNext()) {
      Map.Entry<String, String> paramPair = it.next();
      user = identityService.getBySystemSetting(paramPair.getKey(), paramPair.getValue());
      it.remove();
    }
{'fraction_non_alphanumeric': 0.09995859213250517, 'fraction_numerical': 0.00016563146997929605, 'mean_word_length': 5.355789473684211, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7595022', 'n_tokens_mistral': 3375, 'n_tokens_neox': 3338, 'n_words': 837}