idm-service/src/main/java/org/gcube/service/idm/rest/SocialUsersAPI.java

505 lines
19 KiB
Java

package org.gcube.service.idm.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.controller.LiferayProfileClient;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.rest.ErrorMessages;
import org.gcube.service.rest.ResponseBean;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.validation.ValidationException;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@ManagedBy(IdMManager.class)
@Path("2/users")
// @ResourceGroup("Users APIs")
// @ResourceLabel("Greetings APIs")
// @RequestHeaders({
// @RequestHeader(name = "Authorization", description = "Bearer token, see <a
// href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
// })
public class SocialUsersAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialUsersAPI.class);
/**
* Get the profile associated to the token
*
* @responseExample application/json { "success" : true, "message" : null,
* "result" : { "user_id" : 23769487, "username" :
* "john.smith", "email" : "********", "first_name" : "John",
* "middle_name" : "", "last_name" : "Smith", "fullname" :
* "John Smith", "registration_date" : 1475151491415,
* "user_avatar_url" : "https://******D", "male" : true,
* "job_title" : "", "location_industry" : "no",
* "custom_attrs_map" : null, "email_addresses" : [ ],
* "screen_name" : "john.smith", "user_avatar_id" :
* "https://****sY%3D" } }
* @return the user's profile. The user is the one owning the token
*/
@GET
@Path("get-profile")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The user's profile is reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUserProfile() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
responseBean.setResult(profile);
responseBean.setResult(profile);
responseBean.setSuccess(true);
ObjectMapper objectMapper = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (Exception e) {
logger.error("Unable to retrieve user's profile", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
/**
* Read the user's email address. The user is the one owning the token
*
* @return rhe user's email address
*
* @responseExample application/json {
* "success": true,
* "message": null,
* "result": "email@isti.cnr.it"
* }
*/
@GET
@Path("/get-email")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getCurrentEmail() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String email = owner.getEmail();
responseBean.setResult(email);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve user's email", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Read the user's fullname. The user is the one owning the token
*
* @return the user's fullname
*
* @responseExample application/json {
* "success": true,
* "message": null,
* "result": "FirstName LastName"
* }
*/
@GET
@Path("get-fullname")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The user's fullname is reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUserFullname() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
String fullName = null;
if (owner.isApplication()) {
logger.warn("Trying to access users method via a token different than USER is not allowed");
// only users can use "me"
throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
try {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
fullName = profile.getFullname();
logger.info("Found fullname " + fullName + " for user " + username);
responseBean.setResult(fullName);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve attribute for user.", e);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-all-usernames")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getUsernamesByRole(
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
List<UserRepresentation> users = KCUserController.contextUsers(first, max);
Object result = KCUserController.formatList(users, KCUserController.REPR.compact);
responseBean.setResult(result);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to retrieve users with the requested role", e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-all-fullnames-and-usernames")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getAllUsernamesFullnames(
@QueryParam("emailVerified") Boolean emailVerified,
@QueryParam("enabled") Boolean enabled,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UsersResource users_resource = KCUserController.realmUsersResource();
List<UserRepresentation> users = users_resource.search(emailVerified, first, max, enabled,
true);
Map<String, String> usernamesAndFullnames = new HashMap<String, String>();
users.forEach(user -> usernamesAndFullnames.put(user.getUsername(), user.getEmail()));
responseBean.setResult(usernamesAndFullnames);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve users", e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-usernames-by-role")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getUsernamesByRole(
@QueryParam("role-name") String roleName,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
List<String> usernames = new ArrayList<String>();
try {
List<UserRepresentation> users = KCUserController.searchUsersByRole(roleName, first, max);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
}
}
responseBean.setResult(usernames);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to retrieve users with the requested role", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
@GET
@Path("/user-exists")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response checkUserExists(@QueryParam("username") String username) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UserRepresentation user = KCUserController.getUserByUsername(username);
boolean user_exists = user != null;
responseBean.setResult(user_exists);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to check if user exists with username " + username, e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Read a user's custom attribute. The user is the one owning the token
*
* @param attributeKey The key of the attribute to be read
* @return the user's custom attribute
* @throws ValidationException
*/
@GET
@Path("get-custom-attribute")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "Successful read of the attribute, reported in the 'result' field of the returned object"),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response readCustomAttr(
@QueryParam("username") String username,
@QueryParam("attribute") @NotNull(message = "attribute name is missing") String attributeKey)
throws ValidationException {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (username == null || username.equals("me")) {
if (owner.isApplication()) {
logger.warn("Trying to access users method via a token different than USER is not allowed");
// only users can use "me"
throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
username = owner.getId();
}
if (!AuthController.checkAnyRole(AuthController.ACCESS_READ_ROLES) && !username.equals(owner.getId())) {
// the user can see only his profile
throw new ForbiddenException(ErrorMessages.USER_NOT_AUTHORIZED_PRIVATE);
}
UserRepresentation user = KCUserController.getUserByUsername(username);
Map<String, List<String>> attributes = user.getAttributes();
if (attributes.containsKey(attributeKey)) {
responseBean.setResult(attributes.get(attributeKey));
responseBean.setSuccess(true);
} else {
responseBean.setSuccess(false);
String msg = String.format("Unable to retrieve attribute %s for user %s", attributeKey, user.getUsername());
responseBean.setMessage(msg);
logger.error(msg);
status = Status.NOT_FOUND;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-oauth-profile")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getCurrentOAuthProfile() {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
UserRepresentation user = KCUserController.getUserByUsername(owner.getId());
responseBean.setResult(user);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error(ErrorMessages.CANNOT_RETRIEVE_PROFILE);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Get the list of users having a given global-role, e.g. 'Administrator'.
* (Legacy)
*
* @param roleName the name of the role to be checked (e.g. Administrator)
* @return the list of users having a given global-role
*/
@GET
@Path("get-usernames-by-global-role")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The list is put into the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUsernamesByGlobalRole(
@QueryParam("role-name") String role_name,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (!owner.isApplication()) {
logger.warn(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
throw new ForbiddenException(ErrorMessages.NOT_SERVICE_TOKEN_CONTEXT_USED);
}
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
RolesResource roles_resource = realmResource.roles();
RoleResource r = roles_resource.get(role_name);
List<UserRepresentation> users = r.getUserMembers(first, max);
responseBean.setResult(KCUserController.formatList(users, KCUserController.REPR.username));
responseBean.setSuccess(true);
return Response.status(status).entity(responseBean).build();
}
}