package com.constellio.model.services.security;
import static com.constellio.data.utils.LangUtils.withoutDuplicatesAndNulls;
import static com.constellio.model.entities.schemas.Schemas.ALL_REMOVED_AUTHS;
import static com.constellio.model.entities.schemas.Schemas.ATTACHED_ANCESTORS;
import static com.constellio.model.entities.schemas.Schemas.AUTHORIZATIONS;
import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER;
import static com.constellio.model.entities.schemas.Schemas.IS_DETACHED_AUTHORIZATIONS;
import static com.constellio.model.entities.schemas.Schemas.REMOVED_AUTHORIZATIONS;
import static com.constellio.model.entities.security.global.AuthorizationDeleteRequest.authorizationDeleteRequest;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasExcept;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.where;
import static com.constellio.model.services.security.AuthorizationsServicesRuntimeException.AuthServices_RecordServicesException;
import static com.constellio.model.services.security.AuthorizationsServicesRuntimeException.CannotAddAuhtorizationInNonPrincipalTaxonomy;
import static com.constellio.model.services.security.AuthorizationsServicesRuntimeException.CannotAddUpdateWithoutPrincipalsAndOrTargetRecords;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.dao.services.idGenerator.UniqueIdGenerator;
import com.constellio.data.utils.LangUtils;
import com.constellio.data.utils.LangUtils.ListComparisonResults;
import com.constellio.data.utils.TimeProvider;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.RecordUpdateOptions;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.Group;
import com.constellio.model.entities.records.wrappers.RecordWrapper;
import com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataSchemaTypes;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.security.Authorization;
import com.constellio.model.entities.security.Role;
import com.constellio.model.entities.security.global.AuthorizationAddRequest;
import com.constellio.model.entities.security.global.AuthorizationDeleteRequest;
import com.constellio.model.entities.security.global.AuthorizationDetails;
import com.constellio.model.entities.security.global.AuthorizationModificationRequest;
import com.constellio.model.entities.security.global.AuthorizationModificationResponse;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.logging.LoggingServices;
import com.constellio.model.services.records.RecordProvider;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.records.RecordServicesRuntimeException;
import com.constellio.model.services.records.RecordUtils;
import com.constellio.model.services.records.SchemasRecordsServices;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.LogicalSearchQuery;
import com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.model.services.security.AuthorizationsServicesRuntimeException.CannotDetachConcept;
import com.constellio.model.services.security.AuthorizationsServicesRuntimeException.InvalidPrincipalsIds;
import com.constellio.model.services.security.AuthorizationsServicesRuntimeException.InvalidTargetRecordId;
import com.constellio.model.services.security.AuthorizationsServicesRuntimeException.NoSuchAuthorizationWithId;
import com.constellio.model.services.security.AuthorizationsServicesRuntimeException.NoSuchPrincipalWithUsername;
import com.constellio.model.services.security.roles.Roles;
import com.constellio.model.services.security.roles.RolesManager;
import com.constellio.model.services.security.roles.RolesManagerRuntimeException;
import com.constellio.model.services.taxonomies.TaxonomiesManager;
import com.constellio.model.services.users.UserServices;
public class AuthorizationsServices {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationsServices.class);
ModelLayerFactory modelLayerFactory;
MetadataSchemasManager schemasManager;
private LoggingServices loggingServices;
RolesManager rolesManager;
TaxonomiesManager taxonomiesManager;
RecordServices recordServices;
SearchServices searchServices;
public AuthorizationsServices(ModelLayerFactory modelLayerFactory) {
this.modelLayerFactory = modelLayerFactory;
this.rolesManager = modelLayerFactory.getRolesManager();
this.taxonomiesManager = modelLayerFactory.getTaxonomiesManager();
this.recordServices = modelLayerFactory.newRecordServices();
this.searchServices = modelLayerFactory.newSearchServices();
this.schemasManager = modelLayerFactory.getMetadataSchemasManager();
this.loggingServices = modelLayerFactory.newLoggingServices();
}
SchemasRecordsServices schemas(String collection) {
return new SchemasRecordsServices(collection, modelLayerFactory);
}
public Authorization getAuthorization(String collection, String id) {
if (collection == null) {
throw new IllegalArgumentException("Collection is null");
}
if (id == null) {
throw new IllegalArgumentException("id is null");
}
AuthorizationDetails authDetails = getDetails(collection, id);
if (authDetails == null) {
throw new AuthorizationsServicesRuntimeException.NoSuchAuthorizationWithId(id);
}
List<String> grantedToPrincipals = findAllPrincipalIdsWithAuthorization(authDetails);
return new Authorization(authDetails, grantedToPrincipals);
}
public List<User> getUsersWithGlobalPermissionInCollection(String permission, String collection) {
return getUsersWithGlobalPermissionInCollectionExcludingRoles(permission, collection, new ArrayList<String>());
}
public List<User> getUsersWithGlobalPermissionInCollectionExcludingRoles(String permission, String collection,
List<String> excludingRoles) {
Roles roles = rolesManager.getCollectionRoles(collection);
List<String> rolesGivingPermission = toRolesCodes(roles.getRolesGivingPermission(permission));
rolesGivingPermission.removeAll(excludingRoles);
MetadataSchemaTypes types = schemasManager.getSchemaTypes(collection);
MetadataSchemaType userSchemaType = types.getSchemaType(User.SCHEMA_TYPE);
Metadata userAllRoles = userSchemaType.getDefaultSchema().getMetadata(User.ALL_ROLES);
List<Record> foundRecords = searchServices.search(new LogicalSearchQuery()
.setCondition(from(userSchemaType).where(userAllRoles).isIn(rolesGivingPermission)));
List<User> users = new ArrayList<>();
for (Record record : foundRecords) {
users.add(new User(record, types, roles));
}
return users;
}
public List<User> getUsersWithPermissionOnRecord(String permission, Record concept) {
SchemasRecordsServices schemas = schemas(concept.getCollection());
Roles roles = rolesManager.getCollectionRoles(concept.getCollection());
List<Role> rolesGivingPermission = roles.getRolesGivingPermission(permission);
List<String> rolesCodeGivingPermission = Role.toCodes(rolesGivingPermission);
//TODO tester avec des end/starts
List<String> authsGivingRoleOnConcept = new ArrayList<>();
authsGivingRoleOnConcept.addAll(searchServices.searchRecordIds(from(schemas.authorizationDetails.schemaType())
.where(schemas.authorizationDetails.target()).isIn(concept.getList(ATTACHED_ANCESTORS))
.andWhere(schemas.authorizationDetails.roles()).isIn(rolesCodeGivingPermission)));
//TODO tester avec des remove
//authsGivingRoleOnConcept.removeAll(concept.<String>getList(ALL_REMOVED_AUTHS));
MetadataSchemaTypes types = schemasManager.getSchemaTypes(concept.getCollection());
List<Record> foundRecords = searchServices.search(new LogicalSearchQuery().setCondition(from(schemas.user.schemaType())
.where(schemas.user.alluserauthorizations()).isIn(authsGivingRoleOnConcept)
.orWhere(schemas.user.allroles()).isIn(toRolesCodes(rolesGivingPermission))));
List<User> users = new ArrayList<>();
for (Record record : foundRecords) {
users.add(new User(record, types, roles));
}
return users;
}
public List<String> getUserIdsWithPermissionOnRecord(String permission, Record concept) {
SchemasRecordsServices schemas = schemas(concept.getCollection());
Roles roles = rolesManager.getCollectionRoles(concept.getCollection());
List<Role> rolesGivingPermission = roles.getRolesGivingPermission(permission);
List<String> rolesCodeGivingPermission = Role.toCodes(rolesGivingPermission);
//TODO tester avec des end/starts
List<String> authsGivingRoleOnConcept = new ArrayList<>();
authsGivingRoleOnConcept.addAll(searchServices.searchRecordIds(from(schemas.authorizationDetails.schemaType())
.where(schemas.authorizationDetails.target()).isIn(concept.getList(ATTACHED_ANCESTORS))
.andWhere(schemas.authorizationDetails.roles()).isIn(rolesCodeGivingPermission)));
//TODO tester avec des remove
//authsGivingRoleOnConcept.removeAll(concept.<String>getList(ALL_REMOVED_AUTHS));
MetadataSchemaTypes types = schemasManager.getSchemaTypes(concept.getCollection());
return searchServices.searchRecordIds(new LogicalSearchQuery().setCondition(from(schemas.user.schemaType())
.where(schemas.user.alluserauthorizations()).isIn(authsGivingRoleOnConcept)
.orWhere(schemas.user.allroles()).isIn(toRolesCodes(rolesGivingPermission))));
}
public List<User> getUsersWithPermissionOnRecordExcludingRecordInheritedAuthorizations(String permission, Record concept) {
SchemasRecordsServices schemas = schemas(concept.getCollection());
Roles roles = rolesManager.getCollectionRoles(concept.getCollection());
List<Role> rolesGivingPermission = roles.getRolesGivingPermission(permission);
List<String> rolesCodeGivingPermission = Role.toCodes(rolesGivingPermission);
//TODO tester avec des end/starts
List<String> authsGivingRoleOnConcept = new ArrayList<>();
authsGivingRoleOnConcept.addAll(searchServices.searchRecordIds(from(schemas.authorizationDetails.schemaType())
.where(schemas.authorizationDetails.target()).isEqualTo(concept.getId())
.andWhere(schemas.authorizationDetails.roles()).isIn(rolesCodeGivingPermission)));
//TODO tester avec des remove
//authsGivingRoleOnConcept.removeAll(concept.<String>getList(ALL_REMOVED_AUTHS));
MetadataSchemaTypes types = schemasManager.getSchemaTypes(concept.getCollection());
List<Record> foundRecords = searchServices.search(new LogicalSearchQuery().setCondition(from(schemas.user.schemaType())
.where(schemas.user.alluserauthorizations()).isIn(authsGivingRoleOnConcept)));
List<User> users = new ArrayList<>();
for (Record record : foundRecords) {
users.add(new User(record, types, roles));
}
return users;
}
public List<String> getConceptsForWhichUserHasPermission(String permission, User user) {
if (permission == null) {
throw new IllegalArgumentException("Permission must not be null");
}
if (user == null) {
throw new IllegalArgumentException("User must not be null");
}
SchemasRecordsServices schemas = schemas(user.getCollection());
MetadataSchemaTypes types = schemas.getTypes();
Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(user.getCollection());
if (principalTaxonomy == null) {
return new ArrayList<>();
} else {
List<MetadataSchemaType> schemaTypes = types.getSchemaTypesWithCode(principalTaxonomy.getSchemaTypes());
LogicalSearchQuery query = new LogicalSearchQuery();
query.filteredWithUser(user, permission);
query.setCondition(from(schemaTypes).returnAll());
return searchServices.searchRecordIds(query);
}
}
public List<User> getUsersWithRoleForRecord(String role, Record record) {
List<User> users = new ArrayList<>();
List<Authorization> recordAuths = getRecordAuthorizations(record);
for (Authorization auth : recordAuths) {
if (auth.getDetail().getRoles().contains(role)) {
List<String> principals = auth.getGrantedToPrincipals();
List<Record> principalRecords = recordServices.getRecordsById(auth.getDetail().getCollection(), principals);
if (principals.size() != principalRecords.size()) {
throw new InvalidPrincipalsIds(principalRecords, principals);
}
MetadataSchemaTypes types = schemasManager.getSchemaTypes(record.getCollection());
Roles roles = rolesManager.getCollectionRoles(record.getCollection());
for (Record principalRecord : principalRecords) {
if (principalRecord.getSchemaCode().equals(Group.SCHEMA_TYPE + "_default")) {
List<Record> usersInGroupRecord = getUserRecordsInGroup(principalRecord);
for (Record userRecord : usersInGroupRecord) {
users.add(new User(userRecord, types, roles));
}
} else if (principalRecord.getSchemaCode().equals(User.SCHEMA_TYPE + "_default")) {
users.add(new User(principalRecord, types, roles));
}
}
}
}
return users;
}
public List<Record> getUserRecordsInGroup(Record groupRecord) {
MetadataSchema userSchema = schemasManager.getSchemaTypes(groupRecord.getCollection())
.getSchema(User.SCHEMA_TYPE + "_default");
Metadata userGroupsMetadata = userSchema.getMetadata(User.GROUPS);
LogicalSearchCondition condition = LogicalSearchQueryOperators.from(userSchema)
.where(userGroupsMetadata).isContaining(asList(groupRecord.getId()));
return searchServices.search(new LogicalSearchQuery(condition));
}
/**
* Add an authorization on a record
* @param authorization
* @return
*/
public String add(AuthorizationAddRequest request) {
if (request.getTarget() == null) {
throw new CannotAddUpdateWithoutPrincipalsAndOrTargetRecords();
}
try {
Record record = recordServices.getDocumentById(request.getTarget());
validateCanAssignAuthorization(record);
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
throw new InvalidTargetRecordId(request.getTarget());
}
SolrAuthorizationDetails details = newAuthorizationDetails(request.getCollection(), request.getId(), request.getRoles(),
request.getStart(), request.getEnd()).setTarget(request.getTarget());
return add(new Authorization(details, request.getPrincipals()), request.getExecutedBy());
}
public String add(AuthorizationAddRequest request, User userAddingTheAuth) {
return add(request.setExecutedBy(userAddingTheAuth));
}
/**
* Add an authorization on a record. The authorization will be logged as created by the given user
* @param authorization Authorization to add
* @param userAddingTheAuth
* @return The new authorization's id
*/
private String add(Authorization authorization, User userAddingTheAuth) {
List<Record> principals = getAuthorizationGrantedToPrincipals(authorization);
AuthTransaction transaction = new AuthTransaction();
SolrAuthorizationDetails authorizationDetail = (SolrAuthorizationDetails) authorization.getDetail();
authorizationDetail.setTarget(authorization.getGrantedOnRecord());
transaction.add(authorizationDetail);
String authId = authorizationDetail.getId();
validateDates(authorizationDetail.getStartDate(), authorizationDetail.getEndDate());
addAuthorizationToPrincipals(principals, authId);
transaction.addAll(principals);
executeTransaction(transaction);
if (userAddingTheAuth != null) {
loggingServices.grantPermission(authorization, userAddingTheAuth);
}
return authId;
}
public void execute(AuthorizationDeleteRequest request) {
AuthTransaction transaction = new AuthTransaction();
execute(request, transaction);
executeTransaction(transaction);
}
private static class AuthTransaction extends Transaction {
Set<String> recordsToResetIfNoAuths = new HashSet<>();
List<SolrAuthorizationDetails> authsDetailsToDelete = new ArrayList<>();
@Override
public Record add(Record addUpdateRecord) {
if (getRecordIds().contains(addUpdateRecord.getId())) {
Record currentTransactionRecord = getRecord(addUpdateRecord.getId());
if (currentTransactionRecord != addUpdateRecord) {
throw new RuntimeException("Cannot add the same record twice : " + addUpdateRecord.getId());
}
return addUpdateRecord;
} else {
return super.add(addUpdateRecord);
}
}
}
private void execute(AuthorizationDeleteRequest request, AuthTransaction transaction) {
List<String> authId = asList(request.getAuthId());
LogicalSearchQuery query = new LogicalSearchQuery(fromAllSchemasIn(request.getCollection())
.where(Schemas.AUTHORIZATIONS).isContaining(authId)
.orWhere(REMOVED_AUTHORIZATIONS).isContaining(authId));
List<Record> recordsWithRemovedAuth = searchServices.search(query);
if (request.getExecutedBy() != null) {
try {
Authorization auth = getAuthorization(request.getCollection(), request.getAuthId());
loggingServices.deletePermission(auth, request.getExecutedBy());
} catch (NoSuchAuthorizationWithId e) {
//No problemo
}
}
for (Record record : recordsWithRemovedAuth) {
if (record.getTypeCode().equals(User.SCHEMA_TYPE) || record.getTypeCode().equals(Group.SCHEMA_TYPE)) {
removeAuthorizationToPrincipal(request.getAuthId(), record);
} else {
removeRemovedAuthorizationOnRecord(request.getAuthId(), record);
}
}
transaction.addAll(recordsWithRemovedAuth);
try {
AuthorizationDetails details = getDetails(request.getCollection(), request.getAuthId());
if (details != null) {
transaction.authsDetailsToDelete.add((SolrAuthorizationDetails) details);
}
try {
Record target = recordServices.getDocumentById(details.getTarget());
if (request.isReattachIfLastAuthDeleted() && Boolean.TRUE == target.get(Schemas.IS_DETACHED_AUTHORIZATIONS)) {
transaction.recordsToResetIfNoAuths.add(target.getId());
}
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
//Record does not exist. So nothing to do
}
} catch (NoSuchAuthorizationWithId e) {
//No problemo
}
}
private AuthorizationDetails getDetails(String collection, String id) {
try {
return schemas(collection).getSolrAuthorizationDetails(id);
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
throw new NoSuchAuthorizationWithId(id);
}
}
private void remove(AuthorizationDetails details) {
Record record = ((SolrAuthorizationDetails) details).getWrappedRecord();
recordServices.logicallyDelete(record, User.GOD);
recordServices.physicallyDelete(record, User.GOD);
}
/**
* Modify an authorization on a specific record. The request will be handled differently depending
* if the record is or not the root target of the authorization. This service will never detach/reattach or reset records.
* @param request The request to execute
* @return A response with some informations
*/
public AuthorizationModificationResponse execute(AuthorizationModificationRequest request) {
Authorization authorization = getAuthorization(request.getCollection(), request.getAuthorizationId());
Record record = recordServices.getDocumentById(request.getRecordId());
AuthorizationModificationResponse response = executeWithoutLogging(request, authorization, record);
if (request.getExecutedBy() != null) {
Authorization authorizationAfter;
if (response.getIdOfAuthorizationCopy() != null) {
authorizationAfter = getAuthorization(request.getCollection(), response.getIdOfAuthorizationCopy());
} else {
authorizationAfter = getAuthorization(request.getCollection(), authorization.getDetail().getId());
}
try {
loggingServices.modifyPermission(authorization, authorizationAfter, record, request.getExecutedBy());
} catch (NoSuchAuthorizationWithId e) {
//No problemo
}
}
return response;
}
private List<AuthorizationDetails> getInheritedAuths(Record record) {
SchemasRecordsServices schemas = schemas(record.getCollection());
return (List) schemas.searchSolrAuthorizationDetailss(
where(schemas.authorizationDetails.target()).isNotEqual(record.getId())
.andWhere(schemas.authorizationDetails.target()).isIn(record.getList(ATTACHED_ANCESTORS)));
}
private List<String> toIds(List<AuthorizationDetails> authorizationDetailses) {
List<String> ids = new ArrayList<>();
for (AuthorizationDetails authorizationDetails : authorizationDetailses) {
ids.add(authorizationDetails.getId());
}
return ids;
}
private AuthorizationModificationResponse executeWithoutLogging(AuthorizationModificationRequest request,
Authorization authorization, Record record) {
AuthTransaction transaction = new AuthTransaction();
transaction.add(record);
String authTarget = authorization.getDetail().getTarget();
String authId = authorization.getDetail().getId();
boolean directlyTargetted = authTarget.equals(record.getId());
boolean inherited = !directlyTargetted && record.getList(ATTACHED_ANCESTORS).contains(authTarget);
if (!directlyTargetted && !inherited) {
throw new AuthorizationsServicesRuntimeException.NoSuchAuthorizationWithIdOnRecord(authId, record);
}
AuthorizationModificationResponse response;
if (request.isRemovedOnRecord()) {
if (directlyTargetted) {
execute(authorizationDeleteRequest(authorization.getDetail()).setExecutedBy(request.getExecutedBy()),
transaction);
} else {
removeInheritedAuthorizationOnRecord(authId, record);
}
response = new AuthorizationModificationResponse(false, null, Collections.<String, String>emptyMap());
} else {
if (directlyTargetted) {
executeOnAuthorization(transaction, request, authorization.getDetail(), record,
authorization.getGrantedToPrincipals());
response = new AuthorizationModificationResponse(false, null, Collections.<String, String>emptyMap());
} else {
AuthorizationDetails copy = inheritedToSpecific(transaction, record.getId(), record.getCollection(),
authorization.getDetail().getId());
record.addValueToList(REMOVED_AUTHORIZATIONS, authorization.getDetail().getId());
executeOnAuthorization(transaction, request, copy, record, authorization.getGrantedToPrincipals());
response = new AuthorizationModificationResponse(false, copy.getId(), singletonMap(authId, copy.getId()));
}
}
executeTransaction(transaction);
return response;
}
/**
* Detach a record from its parent, creating specific authorizations that are equal to those before the detach.
* - Future modifications on a parent record won't affect the detached record.
* - If the detached record is reassigned to a new parent, there will be no effects on authorizations
*
* @param record A securized record to detach
* @return A mapping of previous authorization ids to the new authorizations created by this service
*/
public Map<String, String> detach(Record record) {
Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection());
if (principalTaxonomy.getSchemaTypes().contains(record.getTypeCode())) {
throw new CannotDetachConcept(record.getId());
}
if (Boolean.TRUE.equals(record.get(Schemas.IS_DETACHED_AUTHORIZATIONS))) {
return Collections.emptyMap();
} else {
AuthTransaction transaction = new AuthTransaction();
Map<String, String> originalToCopyMap = setupAuthorizationsForDetachedRecord(transaction, record);
transaction.add(record);
executeTransaction(transaction);
return originalToCopyMap;
}
}
/**
* Return all authorizations targetting a given Record, which may be a user or securised Record.
* Authorizations may be inherited or assigned directly to the record
*
* @param recordWrapper User or a securised record
* @return Authorizations
*/
public List<Authorization> getRecordAuthorizations(RecordWrapper recordWrapper) {
return getRecordAuthorizations(recordWrapper.getWrappedRecord());
}
/**
* Return all authorizations targetting a given Record, which may be a user or securised Record.
* Authorizations may be inherited or assigned directly to the record
*
* @param record User or a securised record
* @return Authorizations
*/
public List<Authorization> getRecordAuthorizations(Record record) {
List<String> authIds;
if (User.DEFAULT_SCHEMA.equals(record.getSchemaCode())) {
Metadata allUserAuthorizations = schemasManager.getSchemaTypes(record.getCollection()).getSchema(User.DEFAULT_SCHEMA)
.getMetadata(User.ALL_USER_AUTHORIZATIONS);
authIds = record.getList(allUserAuthorizations);
} else if (Group.DEFAULT_SCHEMA.equals(record.getSchemaCode())) {
authIds = record.getList(Schemas.ALL_AUTHORIZATIONS);
} else {
SchemasRecordsServices schemas = schemas(record.getCollection());
authIds = searchServices.searchRecordIds(from(schemas.authorizationDetails.schemaType())
.where(schemas.authorizationDetails.target()).isIn(record.<String>getList(ATTACHED_ANCESTORS))
.andWhere(Schemas.IDENTIFIER).isNotIn(record.getList(ALL_REMOVED_AUTHS)));
}
List<Authorization> authorizations = new ArrayList<>();
for (String authId : authIds) {
AuthorizationDetails authDetails = getDetails(record.getCollection(), authId);
if (authDetails != null) {
List<String> grantedToPrincipals = findAllPrincipalIdsWithAuthorization(authDetails);
authorizations.add(new Authorization(authDetails, grantedToPrincipals));
} else {
LOGGER.error("Missing authorization '" + authId + "'");
}
}
return authorizations;
}
/**
* Reset a securized record.
* The resetted record will be reattached (inheriting all authorizations) and all its specific authorizations will be lost
* @param record The securized record
*/
public void reset(Record record) {
AuthTransaction transaction = new AuthTransaction();
reset(record, transaction);
executeTransaction(transaction);
}
private void executeTransaction(AuthTransaction transaction) {
transaction.setOptions(RecordUpdateOptions.validationExceptionSafeOptions());
try {
recordServices.execute(transaction);
} catch (Exception e) {
try {
recordServices.executeHandlingImpactsAsync(transaction);
} catch (com.constellio.model.services.records.RecordServicesException e2) {
throw new AuthServices_RecordServicesException(e2);
}
}
for (AuthorizationDetails details : transaction.authsDetailsToDelete) {
remove(details);
}
AuthTransaction transaction2 = new AuthTransaction();
for (String recordIdToResetIfNoAuth : transaction.recordsToResetIfNoAuths) {
Record recordToResetIfNoAuth = recordServices.getDocumentById(recordIdToResetIfNoAuth);
if (getRecordAuthorizations(recordToResetIfNoAuth).isEmpty()) {
reset(recordToResetIfNoAuth, transaction2);
}
}
try {
recordServices.executeHandlingImpactsAsync(transaction2);
} catch (com.constellio.model.services.records.RecordServicesException e) {
throw new AuthServices_RecordServicesException(e);
}
}
public void reset(Record record, AuthTransaction transaction) {
SchemasRecordsServices schemas = schemas(record.getCollection());
List<AuthorizationDetails> authorizationDetailses = new ArrayList<>();
record.set(REMOVED_AUTHORIZATIONS, null);
record.set(IS_DETACHED_AUTHORIZATIONS, false);
transaction.add(record);
for (AuthorizationDetails authorizationDetails : schemas.searchSolrAuthorizationDetailss(
where(schemas.authorizationDetails.target()).isEqualTo(record.getId()))) {
execute(authorizationDeleteRequest(authorizationDetails), transaction);
}
}
public boolean hasDeletePermissionOnPrincipalConceptHierarchy(User user, Record principalTaxonomyConcept,
boolean includeRecords, MetadataSchemasManager schemasManager) {
if (user == User.GOD) {
return true;
}
List<String> paths = principalTaxonomyConcept.getList(Schemas.PATH);
Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(user.getCollection());
validateRecordIsAPrincipalTaxonomyConcept(principalTaxonomyConcept, paths, principalTaxonomy);
int numberOfRecords = 0;
int numberOfRecordsWithUser = 0;
LogicalSearchCondition condition;
if (!includeRecords) {
for (String schemaType : principalTaxonomy.getSchemaTypes()) {
LogicalSearchQuery query = new LogicalSearchQuery();
condition = from(schemasManager.getSchemaTypes(user.getCollection()).getSchemaType(schemaType))
.where(Schemas.PATH).isStartingWithText(paths.get(0)).andWhere(Schemas.LOGICALLY_DELETED_STATUS)
.isFalseOrNull();
query.setCondition(condition);
numberOfRecords += searchServices.searchRecordIds(query).size();
query.filteredWithUserDelete(user);
numberOfRecordsWithUser += searchServices.searchRecordIds(query).size();
}
return numberOfRecords == numberOfRecordsWithUser;
} else {
return hasPermissionOnHierarchy(user, principalTaxonomyConcept, false);
}
}
public boolean hasDeletePermissionOnHierarchy(User user, Record record) {
return hasPermissionOnHierarchy(user, record, false);
}
public boolean hasRestaurationPermissionOnHierarchy(User user, Record record) {
return hasPermissionOnHierarchy(user, record, true);
}
public boolean hasDeletePermissionOnHierarchyNoMatterTheStatus(User user, Record record) {
return hasPermissionOnHierarchy(user, record, null);
}
/**
*Use user.hasReadAccess().on(record) instead
*/
@Deprecated
public boolean canRead(User user, Record record) {
return user.hasReadAccess().on(record);
}
/**
*Use user.hasWriteAccess().on(record) instead
*/
@Deprecated
public boolean canWrite(User user, Record record) {
return user.hasWriteAccess().on(record);
}
/**
*Use user.hasDeleteAccess().on(record) instead
*/
@Deprecated
public boolean canDelete(User user, Record record) {
return user.hasDeleteAccess().on(record);
}
private void executeOnAuthorization(AuthTransaction transaction, AuthorizationModificationRequest request,
AuthorizationDetails authorizationDetails, Record record, List<String> actualPrincipals) {
SchemasRecordsServices schemas = new SchemasRecordsServices(record.getCollection(), modelLayerFactory);
if (request.getNewPrincipalIds() != null) {
if (request.getNewPrincipalIds().isEmpty()) {
throw new CannotAddUpdateWithoutPrincipalsAndOrTargetRecords();
}
List<Record> newPrincipalsRecords = principalToRecords(transaction, schemas, request.getNewPrincipalIds());
List<String> newPrincipals = new RecordUtils().toIdList(newPrincipalsRecords);
RecordProvider recordProvider = new RecordProvider(recordServices, null, newPrincipalsRecords, transaction);
ListComparisonResults<String> results = LangUtils.compare(actualPrincipals, newPrincipals);
for (String newPrincipal : results.getNewItems()) {
Record principalRecord = recordProvider.getRecord(newPrincipal);
principalRecord.addValueToList(AUTHORIZATIONS, authorizationDetails.getId());
transaction.add(principalRecord);
}
for (String removedPrincipal : results.getRemovedItems()) {
Record principalRecord = recordProvider.getRecord(removedPrincipal);
principalRecord.removeValueFromList(AUTHORIZATIONS, authorizationDetails.getId());
transaction.add(principalRecord);
}
}
if (request.getNewAccessAndRoles() != null) {
List<String> accessAndRoles = new ArrayList<String>(request.getNewAccessAndRoles());
if (accessAndRoles.contains(Role.DELETE) && !accessAndRoles.contains(Role.READ)) {
accessAndRoles.add(0, Role.READ);
}
if (accessAndRoles.contains(Role.WRITE) && !accessAndRoles.contains(Role.READ)) {
accessAndRoles.add(0, Role.READ);
}
transaction.add((SolrAuthorizationDetails) authorizationDetails).setRoles(accessAndRoles);
}
if (request.getNewStartDate() != null || request.getNewEndDate() != null) {
LocalDate startDate = request.getNewStartDate() == null ?
authorizationDetails.getStartDate() : request.getNewStartDate();
LocalDate endDate = request.getNewEndDate() == null ? authorizationDetails.getEndDate() : request.getNewEndDate();
validateDates(startDate, endDate);
transaction.add((SolrAuthorizationDetails) authorizationDetails).setStartDate(startDate).setEndDate(endDate);
}
}
private List<Record> principalToRecords(AuthTransaction transaction, SchemasRecordsServices schemas,
List<String> principals) {
List<Record> records = new ArrayList<>();
for (String principal : principals) {
try {
records.add(recordServices.getDocumentById(principal));
} catch (RecordServicesRuntimeException.NoSuchRecordWithId e) {
List<MetadataSchemaType> types = asList(schemas.user.schemaType(), schemas.group.schemaType());
Record record = searchServices.searchSingleResult(from(types).where(schemas.user.username()).isEqualTo(principal)
.orWhere(schemas.group.code()).isEqualTo(principal));
if (record == null) {
throw new NoSuchPrincipalWithUsername(principal);
} else if (transaction.getRecordIds().contains(record.getId())) {
records.add(transaction.getRecord(record.getId()));
} else {
records.add(record);
}
}
}
return records;
}
private List<String> findAllRecordIdsWithAuthorizations(AuthorizationDetails authDetails) {
return new RecordUtils().toIdList(findAllRecordsWithAuthorizations(authDetails));
}
private List<Record> findAllRecordsWithAuthorizations(AuthorizationDetails authDetails) {
List<Record> records = new ArrayList<>();
LogicalSearchQuery query = new LogicalSearchQuery();
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(authDetails.getCollection());
MetadataSchemaType userSchemaType = schemaTypes.getSchemaType(User.SCHEMA_TYPE);
MetadataSchemaType groupSchemaType = schemaTypes.getSchemaType(Group.SCHEMA_TYPE);
query.setCondition(fromAllSchemasExcept(asList(userSchemaType, groupSchemaType)).where(AUTHORIZATIONS)
.isContaining(asList(authDetails.getId())));
records.addAll(searchServices.search(query));
return records;
}
private List<String> findAllPrincipalIdsWithAuthorization(AuthorizationDetails authDetails) {
List<String> principals = new ArrayList<>();
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(authDetails.getCollection());
MetadataSchemaType userSchemaType = schemaTypes.getSchemaType(User.SCHEMA_TYPE);
MetadataSchemaType groupSchemaType = schemaTypes.getSchemaType(Group.SCHEMA_TYPE);
List<Record> allUsers = searchServices.cachedSearch(new LogicalSearchQuery(from(userSchemaType).returnAll()));
List<Record> allGroups = searchServices.cachedSearch(new LogicalSearchQuery(from(groupSchemaType).returnAll()));
if (principals != null) {
for (Record user : allUsers) {
if (user != null && user.getList(AUTHORIZATIONS).contains(authDetails.getId())) {
principals.add(user.getId());
}
}
for (Record group : allGroups) {
if (group != null && group.getList(AUTHORIZATIONS).contains(authDetails.getId())) {
principals.add(group.getId());
}
}
}
return principals;
}
private List<Record> findAllPrincipalsWithAuthorization(AuthTransaction transaction, AuthorizationDetails detail) {
SchemasRecordsServices schemas = schemas(detail.getCollection());
return searchServices.search(new LogicalSearchQuery(
from(asList(schemas.user.schemaType(), schemas.group.schemaType()))
.where(AUTHORIZATIONS).isEqualTo(detail.getId())));
}
private List<Role> getAllAuthorizationRoleForUser(User user) {
List<String> allAuthorizations = new ArrayList<>();
List<Role> allRoleAuthorization = new ArrayList<>();
allAuthorizations.addAll(user.getAllUserAuthorizations());
for (String groupId : user.getUserGroups()) {
Group group = getGroupInCollectionById(groupId, user.getCollection());
allAuthorizations.addAll(group.getAllAuthorizations());
}
for (String id : allAuthorizations) {
List<String> rolesId = getDetails(user.getCollection(), id).getRoles();
allRoleAuthorization = getRolesFromId(rolesId, user.getCollection());
}
return allRoleAuthorization;
}
private List<Role> getAllGlobalRoleForUser(User user) {
List<Role> allGlobalRole = new ArrayList<>();
allGlobalRole.addAll(getRolesFromId(user.getAllRoles(), user.getCollection()));
for (String groupId : user.getUserGroups()) {
Group group = getGroupInCollectionById(groupId, user.getCollection());
allGlobalRole.addAll(getRolesFromId(group.getRoles(), user.getCollection()));
}
return allGlobalRole;
}
private List<Role> getRolesFromId(List<String> rolesId, String collection)
throws RolesManagerRuntimeException {
List<Role> roles = new ArrayList<>();
for (String roleId : rolesId) {
Role role = rolesManager.getRole(collection, roleId);
roles.add(role);
}
return roles;
}
private Group getGroupInCollectionById(String groupId, String collection) {
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(collection);
MetadataSchema groupSchema = schemaTypes.getSchema(Group.SCHEMA_TYPE + "_default");
LogicalSearchCondition condition = LogicalSearchQueryOperators.from(groupSchema).where(IDENTIFIER).is(groupId);
return Group.wrapNullable(searchServices.searchSingleResult(condition), schemaTypes);
}
private boolean hasCollectionPermission(User user) {
return user.hasCollectionReadAccess() || user.hasCollectionWriteAccess() || user.hasCollectionDeleteAccess();
}
private boolean hasPermissionOnHierarchy(User user, Record record, Boolean deleted) {
if (user == User.GOD || user.hasCollectionDeleteAccess()) {
return true;
}
List<String> paths = record.getList(Schemas.PATH);
if (paths.isEmpty()) {
return canDelete(user, record);
}
LogicalSearchQuery query = new LogicalSearchQuery();
LogicalSearchCondition condition;
if (deleted == null) {
condition = fromAllSchemasIn(user.getCollection()).where(Schemas.PATH).isStartingWithText(paths.get(0));
} else if (deleted) {
condition = fromAllSchemasIn(user.getCollection()).where(Schemas.PATH).isStartingWithText(paths.get(0))
.andWhere(Schemas.LOGICALLY_DELETED_STATUS).isTrue();
} else {
condition = fromAllSchemasIn(user.getCollection()).where(Schemas.PATH).isStartingWithText(paths.get(0))
.andWhere(Schemas.LOGICALLY_DELETED_STATUS).isFalseOrNull();
}
query.setCondition(condition);
int numberOfRecords = searchServices.searchRecordIds(query).size();
query.filteredWithUserDelete(user);
int numberOfRecordsWithUser = searchServices.searchRecordIds(query).size();
return (numberOfRecords == numberOfRecordsWithUser && numberOfRecords != 0);
}
private void validateRecordIsAPrincipalTaxonomyConcept(Record principalTaxonomyConcept, List<String> paths,
Taxonomy principalTaxonomy) {
String schemaTypeCode = principalTaxonomyConcept.getSchemaCode().split("_")[0];
if (!paths.get(0).contains(principalTaxonomy.getCode()) || !principalTaxonomy.getSchemaTypes().contains(schemaTypeCode)) {
throw new AuthorizationsServicesRuntimeException.RecordIsNotAConceptOfPrincipalTaxonomy(
principalTaxonomyConcept.getId(), principalTaxonomy.getCode());
}
}
private List<Record> getAuthorizationGrantedToPrincipals(Authorization authorization) {
if (authorization.getGrantedToPrincipals() == null) {
throw new CannotAddUpdateWithoutPrincipalsAndOrTargetRecords();
}
List<String> principalIds = withoutDuplicatesAndNulls(authorization.getGrantedToPrincipals());
if (principalIds.isEmpty() && !authorization.getDetail().isSynced()) {
throw new CannotAddUpdateWithoutPrincipalsAndOrTargetRecords();
}
List<Record> records = recordServices.getRecordsById(authorization.getDetail().getCollection(), principalIds);
if (principalIds.size() != records.size()) {
throw new InvalidPrincipalsIds(records, principalIds);
}
return records;
}
private void validateCanAssignAuthorization(Record record) {
List<String> secondaryTaxonomySchemaTypes = taxonomiesManager.getSecondaryTaxonomySchemaTypes(record.getCollection());
String schemaType = newSchemaUtils().getSchemaTypeCode(record.getSchemaCode());
if (secondaryTaxonomySchemaTypes.contains(schemaType)) {
throw new CannotAddAuhtorizationInNonPrincipalTaxonomy();
}
}
private List<String> toRolesCodes(List<Role> rolesGivingPermission) {
List<String> roleCodes = new ArrayList<>();
for (Role role : rolesGivingPermission) {
roleCodes.add(role.getCode());
}
return roleCodes;
}
private void addAuthorizationToPrincipals(List<Record> principals, String authId) {
for (Record principal : principals) {
addAuthorizationToPrincipal(authId, principal);
}
}
void refreshActivationForAllAuths(List<String> collections) {
for (String collection : collections) {
SchemasRecordsServices schemas = new SchemasRecordsServices(collection, modelLayerFactory);
for (AuthorizationDetails authToDelete : schemas.searchSolrAuthorizationDetailss(
where(schemas.authorizationDetails.endDate()).isLessThan(TimeProvider.getLocalDate()))) {
execute(authorizationDeleteRequest(authToDelete));
}
}
}
Record getRecordWithAuth(String collection, String oldAuthCode) {
return recordServices.getDocumentById(schemas(collection).getSolrAuthorizationDetails(oldAuthCode).getTarget());
}
Map<String, String> setupAuthorizationsForDetachedRecord(AuthTransaction transaction, Record record) {
Map<String, String> originalToCopyMap = new HashMap<>();
List<AuthorizationDetails> inheritedAuthorizations = getInheritedAuths(record);
List<String> removedAuthorizations = record.getList(REMOVED_AUTHORIZATIONS);
for (AuthorizationDetails inheritedAuthorization : inheritedAuthorizations) {
if (!removedAuthorizations.contains(inheritedAuthorization.getId())) {
AuthorizationDetails copy = inheritedToSpecific(transaction, record.getId(), record.getCollection(),
inheritedAuthorization.getId());
if (copy != null) {
originalToCopyMap.put(inheritedAuthorization.getId(), copy.getId());
}
}
}
record.set(REMOVED_AUTHORIZATIONS, new ArrayList<>());
record.set(IS_DETACHED_AUTHORIZATIONS, true);
return originalToCopyMap;
}
AuthorizationDetails inheritedToSpecific(AuthTransaction transaction, String recordId, String collection, String id) {
AuthorizationDetails inherited = getDetails(collection, id);
SolrAuthorizationDetails detail = newAuthorizationDetails(collection, null, inherited.getRoles(),
inherited.getStartDate(), inherited.getEndDate());
detail.setTarget(recordId);
transaction.add(detail);
List<Record> principals = findAllPrincipalsWithAuthorization(transaction, inherited);
if (principals.isEmpty()) {
return null;
} else {
for (Record principal : principals) {
Record principalInTransaction = transaction.getRecord(principal.getId());
if (principalInTransaction == null) {
principalInTransaction = principal;
transaction.add(principal);
}
addAuthorizationToPrincipal(detail.getId(), principalInTransaction);
}
//addAuthorizationToPrincipals(principals, detail.getId());
//transaction.addAll(principals);
return detail;
}
}
void addAuthorizationToPrincipal(String authorizationId, Record record) {
List<Object> recordAuths = new ArrayList<>();
recordAuths.addAll(record.getList(AUTHORIZATIONS));
recordAuths.add(authorizationId);
record.set(AUTHORIZATIONS, recordAuths);
}
void removeAuthorizationToPrincipal(String authorizationId, Record record) {
List<Object> recordAuths = new ArrayList<>();
recordAuths.addAll(record.getList(AUTHORIZATIONS));
recordAuths.remove(authorizationId);
record.set(AUTHORIZATIONS, recordAuths);
}
void removeRemovedAuthorizationOnRecord(String authorizationId, Record record) {
List<Object> recordAuths = new ArrayList<>();
recordAuths.addAll(record.getList(REMOVED_AUTHORIZATIONS));
recordAuths.remove(authorizationId);
record.set(REMOVED_AUTHORIZATIONS, recordAuths);
}
void removeInheritedAuthorizationOnRecord(String authorizationId, Record record) {
List<Object> removedAuths = new ArrayList<>();
removedAuths.addAll(record.getList(REMOVED_AUTHORIZATIONS));
removedAuths.add(authorizationId);
record.set(REMOVED_AUTHORIZATIONS, removedAuths);
}
void validateDates(LocalDate startDate, LocalDate endDate) {
LocalDate now = TimeProvider.getLocalDate();
if ((startDate != null && endDate != null) && startDate.isAfter(endDate)) {
throw new AuthorizationDetailsManagerRuntimeException.StartDateGreaterThanEndDate(startDate, endDate);
}
if (endDate != null && endDate.isBefore(now)) {
throw new AuthorizationDetailsManagerRuntimeException.EndDateLessThanCurrentDate(endDate.toString());
}
}
SchemaUtils newSchemaUtils() {
return new SchemaUtils();
}
private SolrAuthorizationDetails newAuthorizationDetails(String collection, String id, List<String> roles,
LocalDate startDate, LocalDate endDate) {
SolrAuthorizationDetails details = id == null ? schemas(collection).newSolrAuthorizationDetails()
: schemas(collection).newSolrAuthorizationDetailsWithId(id);
return details.setRoles(roles).setStartDate(startDate).setEndDate(endDate);
}
}