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

500 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.Set;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AdminKeycloakController;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.KCClientsController;
import org.gcube.service.idm.controller.KCGroupsController;
import org.gcube.service.idm.controller.KCRolesController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBean;
import org.gcube.service.utils.beans.ResponseBeanMap;
import org.gcube.smartgears.annotations.ManagedBy;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.GroupResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
@ManagedBy(IdMManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("clients")
public class ClientsAPI {
/**
* returns the list of members of the client with the given role
*
* default client is the current client, optional clientId to show a different
* one
*
* default showed role is "Member"
*
* output format can be customized using format_users
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
*
* @param role role to inspect, default Member.
*
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("members")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response clientMembers(
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("role") @DefaultValue("Member") String role_name,
@QueryParam("client_id") String clientId) {
ResponseBean responseBean = new ResponseBean();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
// RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
// ClientResource clientResource = null;
// // select the client by name, or current client if client_name parameter is
// // null;
// if (clientId == null) {
// clientResource = KkClientFactory.getSingleton().getKKClient();
// } else {
// List<ClientRepresentation> clients =
// realmResource.clients().findByClientId(clientId);
// if (clients.size() == 0) {
// throw new NotFoundException();
// }
// String id = clients.get(0).getId();
// clientResource = realmResource.clients().get(id);
// }
// RolesResource roles_resource = clientResource.roles();
// RoleResource role_resource = roles_resource.get(role_name);
// List<UserRepresentation> user_members =
// role_resource.getUserMembers(firstResult, maxResults);
List<UserRepresentation> user_members = KCClientsController.getContextUsersByRole(clientId, role_name,
firstResult, maxResults);
Object result = KCUserController.formatList(user_members, format_users);
responseBean.setResult(result);
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* returns the list of users of the client
* users list is a subset of members list, it's obtained from the group named as
* the context
*
* default client is the current client, optional clientId to show a different
* one
*
* output format can be customized using format_users
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
*
* @param role list of roles to inspect, default Member. can use
* multiple times. use '__all__' to get all roles in the
* client
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("users")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response clientUsers(
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("client_id") String clientId) {
ResponseBean responseBean = new ResponseBean();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
List<UserRepresentation> user_members = KCClientsController.getMemberGroupUsers(clientId, firstResult,
maxResults);
Object result = KCUserController.formatList(user_members, format_users);
responseBean.setResult(result);
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* returns an inspection of the client, showing roles and related members
* default client is the current client, optional clientId to show a different
* one
*
* output format can be customized using format_client, format_users,
* format_roles, format_groups
*
* default showed role is "Member", use role="__all__" to show all roles
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
* @param format_roles response format for users. use "none" for skip
* @param format_client response format for client. use "none" for skip
* @param format_groups response format for group. use "none" for skip
*
* @param role list of roles to inspect, default Member. can use
* multiple times. use '__all__' to get all roles in the
* client
*/
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ClientsAPI.class);
@GET
@Path("info")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response client(
@QueryParam("format_client") @DefaultValue("compact") KCClientsController.REPR format_client,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("format_roles") @DefaultValue("compact") KCRolesController.REPR format_roles,
@QueryParam("format_groups") @DefaultValue("name") KCGroupsController.REPR format_group,
@QueryParam("role") @DefaultValue("Member") final List<String> roles,
@QueryParam("client_id") String clientId) {
ResponseBeanMap responseBean = new ResponseBeanMap();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
// String role_name = "Member";
boolean show_groups = !format_group.equals(KCGroupsController.REPR.none);
boolean show_client = !format_client.equals(KCClientsController.REPR.none);
boolean show_users = !format_users.equals(KCUserController.REPR.none);
boolean show_roles = !format_roles.equals(KCRolesController.REPR.none);
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
ClientResource clientResource = null;
ClientRepresentation client = null;
// select the client by name, or current client if client_name parameter is
// null;
if (clientId == null) {
clientResource = KkClientFactory.getSingleton().getKKClient();
client = clientResource.toRepresentation();
} else {
List<ClientRepresentation> clients = realmResource.clients().findByClientId(clientId);
if (clients.size() == 0) {
throw new NotFoundException();
}
String id = clients.get(0).getId();
clientResource = realmResource.clients().get(id);
client = clientResource.toRepresentation();
}
if (show_client)
responseBean.putResult("client", KCClientsController.formatRepr(client,
format_client));
if (format_client.equals(KCClientsController.REPR.full)) {
UserRepresentation service_account_user = clientResource.getServiceAccountUser();
responseBean.putResult("service_account_user",
KCUserController.formatRepr(service_account_user, format_users));
}
HashMap<String, Object> roles_dict = new HashMap<String, Object>();
responseBean.putResult("roles", roles_dict);
RolesResource roles_resource = clientResource.roles();
List<RoleRepresentation> roles_list = null;
if (roles == null || roles.isEmpty() || roles.contains("__all__")) {
roles_list = roles_resource.list();
} else {
roles_list = new ArrayList<RoleRepresentation>();
for (String role_name : roles) {
try {
RoleResource role_resource = roles_resource.get(role_name);
RoleRepresentation role_repr = role_resource.toRepresentation();
roles_list.add(role_repr);
} catch (Exception e) {
roles_dict.put(role_name, null);
continue;
}
}
}
for (RoleRepresentation role_repr : roles_list) {
String role_name = role_repr.getName();
RoleResource role_resource = roles_resource.get(role_name);
HashMap<String, Object> role_dict = new HashMap<String, Object>();
roles_dict.put(role_name, role_dict);
if (show_roles) {
role_dict.put("role", KCRolesController.formatRepr(role_repr, format_roles));
}
if (show_users) {
List<UserRepresentation> user_members = role_resource.getUserMembers();
role_dict.put("members",
KCUserController.formatList(user_members, format_users));
}
if (show_groups) {
HashMap<String, HashMap<String, Object>> groups_hash = new HashMap<String, HashMap<String, Object>>();
role_dict.put("groups", groups_hash);
Set<GroupRepresentation> groups_members = clientResource.roles().get(role_name).getRoleGroupMembers();
for (GroupRepresentation g_repr : groups_members) {
HashMap<String, Object> group_dict = new HashMap<String, Object>();
groups_hash.put(g_repr.getPath(), group_dict);
group_dict.put("group", KCGroupsController.formatRepr(g_repr, format_group));
if (show_users) {
GroupResource g = realmResource.groups().group(g_repr.getId());
List<UserRepresentation> members = g.members();
group_dict.put("members",
KCUserController.formatList(members, format_users));
}
}
}
}
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
// cannot pass the client name in the path. %2f blocked by tomcat security
// @GET
// @Path("/{name}")
// @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
// public Response clientByName(
// @PathParam("name") String client_name) {
// ResponseBeanMap responseBean = new ResponseBeanMap();
// try {
// RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
// List<ClientRepresentation> clients =
// realm.clients().findByClientId(client_name);
// if (clients.size() == 0) {
// throw new NotFoundException();
// }
// String id = clients.get(0).getId();
// ClientResource clientResource = realm.clients().get(id);
// ClientRepresentation client = clientResource.toRepresentation();
// UserRepresentation service_account_user =
// clientResource.getServiceAccountUser();
// responseBean.putResult("client", client);
// responseBean.putResult("service_account_user", service_account_user);
// Set<GroupRepresentation> members =
// clientResource.roles().get("Member").getRoleGroupMembers();
// responseBean.putResult("members", members);
// responseBean.setSuccess(true);
// ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
// String jsonData = objectMapper.writeValueAsString(responseBean);
// return Response.ok(jsonData).build();
// } catch (JsonProcessingException e) {
// e.printStackTrace();
// throw new InternalServerErrorException(e);
// }
// }
public class ClientFromTemplateParams {
String client_name;
String client_id;
String context;
}
@POST
@Path("fromTemplate/{name}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClientFromTemplate(
@PathParam("name") String template_name,
ClientFromTemplateParams params) {
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
List<ClientRepresentation> clients = realm.clients().findByClientId(template_name);
if (clients.size() == 0) {
throw new NotFoundException();
}
String id = clients.get(0).getId();
ClientResource clientResource = realm.clients().get(id);
ClientRepresentation client = clientResource.toRepresentation();
UserRepresentation template_account_user = clientResource.getServiceAccountUser();
client.setId(params.client_id);
client.setName(params.client_name);
responseBean.putResult("client", client);
responseBean.putResult("service_account_user", template_account_user);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
@POST
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClientFromTemplate(ClientRepresentation client) {
logger.info("received client {}", client);
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
logger.info("received client {}", client);
ClientRepresentation newClient = AdminKeycloakController.createClient(client);
responseBean.putResult("input", client);
responseBean.putResult("created", newClient);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
@POST
@Path("/")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClient(ClientRepresentation client) {
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
logger.info("received client {}", client);
ClientRepresentation newClient = AdminKeycloakController.createClient(client);
responseBean.putResult("input", client);
responseBean.putResult("created", newClient);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
}