// Check create, update or delete authorizations on all the entities of a dataset.
        // dataset : the dataset to check
        // claims : user claims to check upon
        // entitiesToIgnore : for these entities, the check should be skipped
        // mode : define if an entity not authorized should be removed from the dataset or an exception thrown
        public PermissionLevel CheckWriteAuthorizationsOnDataSet(IObjectsDataSet saveset, UserClaims claims, Parameters parameters, out string message)
        {
            // Work on a clone of the dataset so as not to mess with the original (causes save problems otherwise, e.g. if main entity !IsDirty)
            var dataset = saveset.CloneDirtyObjects();

            message = null;
            SecurityPredicate predicate = null;

            foreach (var entity in dataset.GetAllObjects())
            {
                // get the instance of the entity authorizations class
                var entityAuthorizations = GetEntityAuthorizations(entity);

                // case delete
                if (entity.IsMarkedForDeletion)
                {
                    var permissionLevel = CanDelete(entity, claims, out message, out predicate);

                    if (permissionLevel != PermissionLevel.Authorized)
                    {
                        string explanation = Explain(EntityAccessEnum.DELETE, entityAuthorizations.EntityDisplayName);
                        message = FormatAccessDeniedMessage(explanation, predicate);
                        return(PermissionLevel.Denied);
                    }
                }
                // case create / update
                else
                {
                    PermissionLevel permissionLevel;

                    if (entity.IsNew)
                    {
                        permissionLevel = CanCreate(entity, claims, out message, out predicate);
                    }
                    else if (entity.IsDirty)
                    {
                        permissionLevel = CanUpdate(entity, claims, out message, out predicate);

                        if (permissionLevel != PermissionLevel.Authorized && AppSettings.Get <bool>("DataSetAuthorizationCheckModeRemoveFromDataSet"))
                        {
                            saveset.RemoveObject(entity);
                            continue;
                        }
                    }
                    else
                    {
                        continue;
                    }

                    if (permissionLevel != PermissionLevel.Authorized)
                    {
                        string explanation = Explain(entity.IsNew ? EntityAccessEnum.CREATE : EntityAccessEnum.UPDATE, entityAuthorizations.EntityDisplayName);
                        message = FormatAccessDeniedMessage(explanation, predicate);
                        return(PermissionLevel.Denied);
                    }
                }
            }

            return(PermissionLevel.Authorized);
        }
        private string FormatAccessDeniedMessage(string explanation, SecurityPredicate predicate)
        {
            if (predicate == null)
            {
                return(explanation);
            }

            return($"{explanation}{(String.IsNullOrEmpty(predicate.Message) || predicate.Message.Contains(explanation) ? String.Empty : $": {predicate.Message}")}");
        }
        // Check read authorization on all the entities of a dataset. usefull when a get, getcollection or save action is configured with includes, to make sure related objects can be read for the given user claims.
        // dataset : the dataset to check
        // entitiesToIgnore : for these entities, the check should be skipped
        // mode : define if an entity not authorized should be removed from the dataset or an exception thrown
        public PermissionLevel CheckReadAuthorizationsOnDataSet(IObjectsDataSet dataset, UserClaims claims, IEnumerable <IDataObject> entitiesToIgnore, DataSetAuthorizationCheckMode mode, Parameters parameters, out string message)
        {
            message = null;
            SecurityPredicate predicate = null;

            // Work on a clone of the dataset so as not to mess with the original (causes save problems otherwise, e.g. if main entity !IsDirty)
            dataset = dataset.Clone();

            foreach (var entity in dataset.GetAllObjects())
            {
                var permissionLevel = CanRead(entity, claims, out message, out predicate);

                if (permissionLevel != PermissionLevel.Authorized)
                {
                    if (mode == DataSetAuthorizationCheckMode.RemoveFromDataSet)
                    {
                        dataset.RemoveObject(entity);
                        continue;
                    }
                    else
                    {
                        string explanation = Explain(EntityAccessEnum.READ, GetEntityAuthorizations(entity).EntityDisplayName);
                        message = FormatAccessDeniedMessage(explanation, predicate);
                        return(PermissionLevel.Denied);
                    }
                }

                if (predicate != null)
                {
                    // If the security filter needs related data, load it first:
                    if (!String.IsNullOrEmpty(predicate.Includes))
                    {
                        DataObjectHelper.RecurseLoadIncludes(entity, parameters, predicate.Includes, skipSecurity: true);
                    }

                    if (!DoesPredicateAccept(entity, predicate.Filter, predicate.Includes, EntityAccessEnum.READ))
                    {
                        if (mode == DataSetAuthorizationCheckMode.RemoveFromDataSet)
                        {
                            dataset.RemoveObject(entity);
                            continue;
                        }
                        else
                        {
                            string explanation = Explain(EntityAccessEnum.READ, GetEntityAuthorizations(entity).EntityDisplayName);
                            message = FormatAccessDeniedMessage(explanation, predicate);
                            return(PermissionLevel.Denied);
                        }
                    }
                }
            }

            return(PermissionLevel.Authorized);
        }
        public bool IsAuthorized(
            IDataObject entity,
            SecurityPredicate predicate,
            EntityAccessEnum action,
            ref string message,
            Parameters parameters = null)
        {
            if (parameters == null)
            {
                // connect to existing transaction by using the existing parameters
                var transaction = ApplicationSettings.Resolve <ITransactionProvider>().GetTransaction(parameters) as DatabaseDataProviderTransaction;
                parameters = transaction.Parameters;
                // this following line is probably no necessary. just for safety
                parameters = parameters ?? new Parameters();
            }

            var entityAuthorizations = GetEntityAuthorizations(entity);

            // If the security filter needs related data, load related data first:
            if (!String.IsNullOrEmpty(predicate.Includes))
            {
                DataObjectHelper.RecurseLoadIncludes(entity, parameters, predicate.Includes, skipSecurity: true);
            }

            // First check the submitted data : is submitted data authorized?
            if (predicate.IsEvaluateDataset)
            {
                if (!DoesPredicateAccept(entity, predicate.DatasetFilter, predicate.Includes, action))
                {
                    string explanation = Explain(action, entityAuthorizations.EntityDisplayName);
                    message = FormatAccessDeniedMessage(explanation, predicate);
                    return(false);
                }
            }

            // Then check existing data in database
            if (predicate.IsEvaluateDatabase && !entity.IsNew)
            {
                // Important: We fetch a fresh copy (including the includes) to eliminate possibility of hacker messing with the dataset
                dynamic provider       = ApplicationSettings.Container.Resolve <IEntityDataProvider>().GetDataProviderForEntity(entity);
                var     existingEntity = provider.Get(entity, null, includes: String.IsNullOrEmpty(predicate.Includes) ? null : predicate.Includes.Split(',').ToList(), parameters: parameters, skipSecurity: true);

                if (!DoesPredicateAccept(existingEntity, predicate.DatasetFilter, predicate.Includes, action))
                {
                    string explanation = Explain(action, entityAuthorizations.EntityDisplayName);
                    message = FormatAccessDeniedMessage(explanation, predicate);
                    return(false);
                }
            }

            return(true);
        }
 public PermissionLevel CanDelete(IDataObject entity, UserClaims claims, out string message, out SecurityPredicate predicate)
 {
     message   = null;
     predicate = null;
     return(PermissionLevel.NotSet);
 }
        public PermissionLevel CanCreate(IDataObject entity, UserClaims claims, out string message, out SecurityPredicate predicate)
        {
            message   = null;
            predicate = null;

            // If not authenticated and no role assigned to anonymous users
            if (claims.IsAuthenticated == false && claims.Roles.Count == 0)
            {
                return(PermissionLevel.Denied);
            }

            PermissionLevel result = PermissionLevel.NotSet;

            // get the per-entity authorizations class so we can check for entity-specific permissions overrides
            var entityAuthorizations = GetEntityAuthorizations(entity);

            // Check override access rule first
            if (entityAuthorizations != null)
            {
                result = entityAuthorizations.CanCreate(entity, claims, out message, out predicate);

                if (result == PermissionLevel.Denied)
                {
                    string explanation = Explain(EntityAccessEnum.CREATE, entityAuthorizations.EntityDisplayName);
                    message = FormatAccessDeniedMessage(explanation, predicate);
                    result  = PermissionLevel.Denied;
                }
            }

            // If no override rule found, check default entity rules for an authorized role
            if (result == PermissionLevel.NotSet)
            {
                // Roles "Administrator", "User" have default authorized CREATE access to all entities
                if (claims.Roles.Intersect(new List <string> {
                    "Administrator", "User"
                }).Any())
                {
                    result = PermissionLevel.Authorized;
                }
                else
                {
                    message = Explain(EntityAccessEnum.CREATE, entityAuthorizations.EntityDisplayName);
                    result  = PermissionLevel.Denied;
                }
            }

            return(result);
        }