package org.ovirt.engine.api.restapi.resource.aaa;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.api.model.BaseResource;
import org.ovirt.engine.api.model.Group;
import org.ovirt.engine.api.model.Groups;
import org.ovirt.engine.api.resource.aaa.GroupResource;
import org.ovirt.engine.api.resource.aaa.GroupsResource;
import org.ovirt.engine.api.restapi.resource.AbstractBackendCollectionResource;
import org.ovirt.engine.api.restapi.resource.ResourceConstants;
import org.ovirt.engine.api.restapi.util.QueryHelper;
import org.ovirt.engine.api.restapi.utils.DirectoryEntryIdUtils;
import org.ovirt.engine.api.restapi.utils.aaa.AuthzUtils;
import org.ovirt.engine.core.aaa.DirectoryGroup;
import org.ovirt.engine.core.common.action.AddGroupParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.aaa.DbGroup;
import org.ovirt.engine.core.common.interfaces.SearchType;
import org.ovirt.engine.core.common.queries.DirectoryIdQueryParameters;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
/**
* This resource corresponds to groups that have been looked up in some directory accessible to the engine and then
* added to the engine database. Groups can be added and removed from the collection, and this will add or remove them
* from the database (not from the underlying directory).
*/
public class BackendGroupsResource
extends AbstractBackendCollectionResource<Group, DbGroup>
implements GroupsResource {
private static final String GROUPS_SEARCH_PATTERN = "grpname != \"\"";
private static final String AND_SEARCH_PATTERN = " and ";
public BackendGroupsResource() {
super(Group.class, DbGroup.class);
}
/**
* This method calculates the search pattern that will be used to perform the search of database groups during the
* execution of the {@code list} operation.
*/
private String getSearchPattern() {
String userProvidedPattern = QueryHelper.getConstraint(httpHeaders, uriInfo, "", modelType);
return userProvidedPattern.equals("Groups : ") ?
userProvidedPattern + GROUPS_SEARCH_PATTERN
:
userProvidedPattern + AND_SEARCH_PATTERN + GROUPS_SEARCH_PATTERN;
}
/**
* Determine what is the name of the directory that corresponds to the given group model. It may contained in the
* model directly, or it can be embedded in the name.
*
* @param group the model of the group
* @param authzProvidersNames
* list of existing authz provider names, including the returned provider name, if exists in the list
* @return the name of the directory or {@code null} if the group can't be determined
*/
private String getAuthzProviderName(Group group, Collection<String> authzProvidersNames) {
if (group.isSetDomain() && group.getDomain().isSetName()) {
return group.getDomain().getName();
}
else if (group.isSetDomain() && group.getDomain().isSetId()) {
for (String domain : authzProvidersNames) {
Guid domainId = new Guid(domain.getBytes(StandardCharsets.UTF_8), true);
if (domainId.toString().equals(group.getDomain().getId())) {
return domain;
}
}
throw new WebFaultException(
null,
"Domain: '" + group.getDomain().getId().toString() + "' does not exist.",
Response.Status.BAD_REQUEST);
}
return AuthzUtils.getAuthzNameFromEntityName(group.getName(), authzProvidersNames);
}
/**
* This method calculates the search pattern used to search for the directory group that will be added to the
* database when performing the {@code add} operation.
*
* @param groupname the name of the user that will be searched in the
* directory
* @param domain the name of the directory where the search will be
* performed
*/
private String getDirectoryGroupSearchPattern(String groupname, String domain) {
String constraint = QueryHelper.getConstraint(httpHeaders, uriInfo, DbGroup.class, false);
final StringBuilder sb = new StringBuilder(128);
sb.append(MessageFormat.format(ResourceConstants.AAA_GROUPS_SEARCH_TEMPLATE, domain, ""));
sb.append(StringUtils.isEmpty(constraint) ?
"name=" + groupname
:
constraint);
return sb.toString();
}
private Groups mapDbGroupCollection(List<DbGroup> entities) {
Groups collection = new Groups();
for (DbGroup entity : entities) {
Group group = map(entity);
group = populate(group, entity);
group = addLinks(group, BaseResource.class);
collection.getGroups().add(group);
}
return collection;
}
@Override
public GroupResource getGroupResource(String id) {
return inject(new BackendGroupResource(id, this));
}
@Override
public Groups list() {
if (isFiltered()) {
return mapDbGroupCollection(getBackendCollection(VdcQueryType.GetAllDbGroups, new VdcQueryParametersBase(), SearchType.DBGroup));
}
else {
return mapDbGroupCollection(getBackendCollection(SearchType.DBGroup, getSearchPattern()));
}
}
@Override
public Response add(Group group) {
List<String> authzProvidersNames = getBackendCollection(
String.class,
VdcQueryType.GetDomainList,
new VdcQueryParametersBase());
validateParameters(group, "name");
if (AuthzUtils.getAuthzNameFromEntityName(group.getName(), authzProvidersNames) == null) {
validateParameters(group, "domain.id|name");
}
String directoryName = getAuthzProviderName(group, authzProvidersNames);
DirectoryGroup directoryGroup = findDirectoryGroup(directoryName, group);
if (directoryGroup == null) {
return Response.status(Status.BAD_REQUEST)
.entity("No such group: " + group.getName() + " in directory " + directoryName)
.build();
}
AddGroupParameters parameters = new AddGroupParameters();
parameters.setGroupToAdd(new DbGroup(directoryGroup));
QueryIdResolver<Guid> resolver = new QueryIdResolver<>(VdcQueryType.GetDbGroupById, IdQueryParameters.class);
return performCreate(VdcActionType.AddGroup, parameters, resolver, BaseResource.class);
}
/**
* Find the directory user that corresponds to the given model.
*
* @param directoryName the name of the directory where to perform the search
* @param groupModel the group model
* @return the requested directory group or {@code null} if no such group exists
*/
private DirectoryGroup findDirectoryGroup(String directoryName, Group groupModel) {
// Try to find a group that matches the identifier contained in the model:
String namespace = groupModel.getNamespace();
if (groupModel.isSetId()) {
return getGroupById(directoryName, namespace, groupModel.getId());
} else if (groupModel.isSetDomainEntryId()) {
return getGroupById(directoryName, namespace, groupModel.getDomainEntryId());
} else if (groupModel.isSetName()) {
return getEntity(
DirectoryGroup.class,
SearchType.DirectoryGroup,
getDirectoryGroupSearchPattern(AuthzUtils.getEntityNameWithoutAuthz(groupModel.getName(), directoryName), directoryName)
);
}
return null;
}
private DirectoryGroup getGroupById(String directoryName, String namespace, String groupId) {
try {
groupId = DirectoryEntryIdUtils.decode(groupId);
}
catch (IllegalArgumentException exception) {
return null;
}
return getEntity(
DirectoryGroup.class,
VdcQueryType.GetDirectoryGroupById,
new DirectoryIdQueryParameters(directoryName, namespace, groupId),
groupId,
true);
}
}