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 string ExplainAccessDenied(EntityAccessEnum change, string entityDisplayName) { switch (change) { case EntityAccessEnum.READ: return($"Access denied: You're not authorized to view this {entityDisplayName} data"); case EntityAccessEnum.CREATE: return($"Access denied: You're not authorized to create this {entityDisplayName} data"); case EntityAccessEnum.UPDATE: return($"Access denied: You're not authorized to modify this {entityDisplayName} data"); case EntityAccessEnum.DELETE: return($"Access denied: You're not authorized to delete this {entityDisplayName} data"); default: return("Access denied"); } }
private bool DoesPredicateAccept(IDataObject entity, LambdaExpression predicate, string includes, EntityAccessEnum access) { try { return(System.Linq.Dynamic.DynamicExpression.ParseAndExecutePredicate(entity, predicate)); } catch (Exception e) { // Development time exception - could make this a configuration thing if we want to be sure users never see these messages. // But this is a lot more helpful than allowing the default exception to bubble up - no way to know what's wrong currently. throw new GOServerException("securityPredicateExecutionFailure", $@"Error executing {access.ToString().ToUpper()} security predicate for entity '{entity.GetType().Name.Replace("DataObject", "")}'. Predicate: {predicate.ToString()} Includes: {(String.IsNullOrEmpty(includes) ? "(none)" : includes)} Error: {(e.InnerException == null ? e.Message : e.InnerException.Message)} Diagnosis: * Is the include path correct? If not, please raise a support ticket. * Can any of the related entities along the include path be null? And if so, have you remembered to check for nulls in the predicate? " , e); } }
private static string Explain(EntityAccessEnum change, string entityDisplayName) { return(ApplicationSettings.Container.Resolve <IAuthentication>().ExplainAccessDenied(change, entityDisplayName)); }