internal IEnumerable <Dictionary <string, object> > GetItems(IInstanceContext context, string contentType, IBlock ctxBlock, string appPath = null) { var wrapLog = Log.Call($"get entities type:{contentType}, path:{appPath}"); // if app-path specified, use that app, otherwise use from context var appIdentity = AppFinder.GetAppIdFromPathOrContext(appPath, ctxBlock); // get the app - if we have the context from the request, use that, otherwise generate full app var app = ctxBlock == null ? Factory.Resolve <Apps.App>().Init(appIdentity, Log) : GetApp(appIdentity.AppId, ctxBlock); // verify that read-access to these content-types is permitted var permCheck = new MultiPermissionsTypes().Init(context, app, contentType, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var result = new EntityApi(appIdentity.AppId, permCheck.EnsureAny(GrantSets.ReadDraft), Log) .GetEntities(contentType) ?.ToList(); wrapLog("found: " + result?.Count); return(result); }
/// <summary> /// Preprocess security / context, then get the item based on an passed in method, /// ...then process/finish /// </summary> /// <returns></returns> internal Dictionary <string, object> GetOne(IInstanceContext context, IBlock ctxBlock, string contentType, Func <EntityApi, IEntity> getOne, string appPath) { Log.Add($"get and serialize after security check type:{contentType}, path:{appPath}"); // if app-path specified, use that app, otherwise use from context var appIdentity = AppFinder.GetAppIdFromPathOrContext(appPath, ctxBlock); var entityApi = new EntityApi(appIdentity.AppId, true, Log); var itm = getOne(entityApi); var permCheck = new MultiPermissionsItems().Init(context, GetApp(appIdentity.AppId, ctxBlock), itm, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } // in case draft wasn't allow, get again with more restricted permissions if (!permCheck.EnsureAny(GrantSets.ReadDraft)) { entityApi = new EntityApi(appIdentity.AppId, false, Log); itm = getOne(entityApi); } return(InitEavAndSerializer(appIdentity.AppId, ctxBlock?.EditAllowed ?? false).Convert(itm)); }
public IList <AdamItemDto> Folder(string parentSubfolder, string newFolder) { var logCall = Log.Call <IList <AdamItemDto> >($"get folders for subfld:{parentSubfolder}, new:{newFolder}"); if (AdamContext.Security.UserIsRestricted && !AdamContext.Security.FieldPermissionOk(GrantSets.ReadSomething)) { return(null); } // get root and at the same time auto-create the core folder in case it's missing (important) var folder = AdamContext.AdamRoot.Folder(); // try to see if we can get into the subfolder - will throw error if missing if (!string.IsNullOrEmpty(parentSubfolder)) { folder = AdamContext.AdamRoot.Folder(parentSubfolder, false); } // validate that dnn user have write permissions for folder in case dnn file system is used (usePortalRoot) if (AdamContext.UseSiteRoot && !AdamContext.Security.CanEditFolder(folder)) { throw HttpException.PermissionDenied("can't create new folder - permission denied"); } var newFolderPath = string.IsNullOrEmpty(parentSubfolder) ? newFolder : Path.Combine(parentSubfolder, newFolder).Replace("\\", "/"); // now access the subfolder, creating it if missing (which is what we want AdamContext.AdamRoot.Folder(newFolderPath, true); return(logCall("ok", ItemsInField(parentSubfolder))); }
public dynamic DialogSettings(IInstanceContext context, ContextBuilderBase contextBuilder, int appId) { IApp app = null; // if we have an appid (we don't have it in an install-new-apps-scenario) check permissions if (appId != 0 && appId != Eav.Constants.AppIdEmpty) { var appAndPerms = new MultiPermissionsApp().Init(context, GetApp(appId, null), Log); if (!appAndPerms.ZoneIsOfCurrentContextOrUserIsSuper(out var error)) { throw HttpException.PermissionDenied(error); } app = appAndPerms.App; } var cb = contextBuilder.InitApp(app?.ZoneId, app); return(new { // TODO: Deprecate PARAMS these properties as soon as old UI is gone //IsContent = app?.AppGuid == "Default", //Language = psCurrent.CultureCode, //LanguageDefault = psCurrent.DefaultLanguage, //AppPath = app?.Path, //GettingStartedUrl = cb.GettingStartedUrl(), // END TODO Context = cb.Get(Ctx.All), }); }
public IEnumerable <ViewDto> ViewUsage(IInstanceContext context, int appId, Guid guid, Func <List <IView>, List <BlockConfiguration>, IEnumerable <ViewDto> > finalBuilder) { var wrapLog = Log.Call <IEnumerable <ViewDto> >($"{appId}, {guid}"); // extra security to only allow zone change if host user var permCheck = new MultiPermissionsApp().Init(context, GetApp(appId, null), Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var cms = new CmsRuntime(appId, Log, true); // treat view as a list - in case future code will want to analyze many views together var views = new List <IView> { cms.Views.Get(guid) }; var blocks = cms.Blocks.AllWithView(); Log.Add($"Found {blocks.Count} content blocks"); var result = finalBuilder(views, blocks); return(wrapLog("ok", result)); }
private void ThrowIfNotSuperuser() { if (!PortalSettings.UserInfo.IsSuperUser) { throw HttpException.PermissionDenied("requires Superuser permissions"); } }
public DialogContextStandalone DialogSettings(int appId) { // reset app-id if we get a info-token like -100 if (appId < 0) { appId = Eav.Constants.AppIdEmpty; } var appContext = appId != Eav.Constants.AppIdEmpty ? _ctxResolver.App(appId) : null; var context = appContext ?? _ctxResolver.Site(); // if we have an appid (we don't have it in an install-new-apps-scenario) check permissions if (appContext != null) { var appAndPerms = ServiceProvider.Build <MultiPermissionsApp>().Init(appContext, appContext.AppState, Log); if (!appAndPerms.ZoneIsOfCurrentContextOrUserIsSuper(out var error)) { throw HttpException.PermissionDenied(error); } } var cb = _uiContextBuilder.SetZoneAndApp(context.Site.ZoneId, appContext?.AppState); return(new DialogContextStandalone { Context = cb.Get(Ctx.All), }); }
protected void ThrowIfNotAllowedInApp(List <Grants> requiredGrants, IAppIdentity alternateApp = null) { var permCheck = ServiceProvider.Build <MultiPermissionsApp>().Init(ContextOfBlock, alternateApp ?? ContextOfBlock.AppState, Log); if (!permCheck.EnsureAll(requiredGrants, out var error)) { throw HttpException.PermissionDenied(error); } }
internal static bool DestinationIsInItem(Guid guid, string field, string path, out HttpExceptionAbstraction preparedException) { var inAdam = Sxc.Adam.Security.PathIsInItemAdam(guid, field, path); preparedException = inAdam ? null : HttpException.PermissionDenied("Can't access a resource which is not part of this item."); return(inAdam); }
internal Dictionary <string, object> CreateOrUpdate(IInstanceContext context, IBlock ctxBlock, string contentType, Dictionary <string, object> newContentItem, int?id = null, string appPath = null) { Log.Add($"create or update type:{contentType}, id:{id}, path:{appPath}"); // if app-path specified, use that app, otherwise use from context var appIdentity = AppFinder.GetAppIdFromPathOrContext(appPath, ctxBlock); // Check that this ID is actually of this content-type, // this throws an error if it's not the correct type var itm = id == null ? null : new EntityApi(appIdentity.AppId, true, Log).GetOrThrow(contentType, id.Value); var ok = itm == null ? new MultiPermissionsTypes() .Init(context, GetApp(appIdentity.AppId, ctxBlock), contentType, Log) .EnsureAll(Grants.Create.AsSet(), out var error) : new MultiPermissionsItems().Init(context, GetApp(appIdentity.AppId, ctxBlock), itm, Log) .EnsureAll(Grants.Update.AsSet(), out error); if (!ok) { throw HttpException.PermissionDenied(error); } // Convert to case-insensitive dictionary just to be safe! newContentItem = new Dictionary <string, object>(newContentItem, StringComparer.OrdinalIgnoreCase); // Now create the cleaned up import-dictionary so we can create a new entity var cleanedNewItem = new AppContentEntityBuilder(Log) .CreateEntityDictionary(contentType, newContentItem, appIdentity.AppId); var userName = context.User.IdentityToken; // try to create // 2020-08-21 disabled publish check, don't think it's relevant in API mode // var publish = Factory.Resolve<IPagePublishing>().Init(Log); // var enablePublish = publish.IsEnabled(context.Container.Id); var currentApp = GetApp(appIdentity.AppId, ctxBlock); //Factory.Resolve<Apps.App>().Init(appIdentity, // ConfigurationProvider.Build(false, false, // ctxBlockBuilder?.Block.Data.Configuration.LookUps), true, Log); if (id == null) { var entity = currentApp.Data.Create(contentType, cleanedNewItem, userName); id = entity.EntityId; } else { currentApp.Data.Update(id.Value, cleanedNewItem, userName); } return(InitEavAndSerializer(appIdentity.AppId, ctxBlock?.EditAllowed ?? false) .Convert(currentApp.Data.List.One(id.Value))); }
public bool Publish(int id) { Log.Add($"try to publish id #{id}"); if (!new MultiPermissionsApp().Init(_context, _block.App, Log).EnsureAll(GrantSets.WritePublished, out var error)) { throw HttpException.PermissionDenied(error); } new AppManager(_block.App, Log).Entities.Publish(id); return(true); }
public bool Publish(string part, int index) { Log.Add($"try to publish #{index} on '{part}'"); if (!new MultiPermissionsApp().Init(_context, _block.App, Log) .EnsureAll(GrantSets.WritePublished, out var error)) { throw HttpException.PermissionDenied(error); } return(BlockEditorBase.GetEditor(_block).Publish(part, index)); }
protected MultiPermissionsItems ThrowIfNotAllowedInItem(IEntity itm, List <Grants> requiredGrants, IAppIdentity alternateApp = null) { var permCheck = ServiceProvider.Build <MultiPermissionsItems>().Init(Context, alternateApp ?? Context.AppState, itm, Log); if (!permCheck.EnsureAll(requiredGrants, out var error)) { throw HttpException.PermissionDenied(error); } return(permCheck); }
public Guid?SaveTemplateId(int templateId, bool forceCreateContentGroup) { var permCheck = new MultiPermissionsApp().Init(_context, _block.App, Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } return(BlockEditorBase.GetEditor(_block).SaveTemplateId(templateId, forceCreateContentGroup)); }
internal bool UserIsPermittedOnField(List <Grants> requiredPermissions, out HttpExceptionAbstraction preparedException) { // check field permissions, but only for non-publish-data if (UserIsRestricted && !FieldPermissionOk(requiredPermissions)) { preparedException = HttpException.PermissionDenied("this field is not configured to allow uploads by the current user"); return(false); } preparedException = null; return(true); }
public dynamic Usage(IInstanceContext context, IApp app, Guid guid) { var permCheck = new MultiPermissionsApp().Init(context, app, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var appData = permCheck.App.Data; var item = appData.List.One(guid); var relationships = item.Relationships.AllRelationships; // var result = relationships.Select(r => new EntityInRelationDto(r.)) // todo: don't forget Metadata relationships return(null); }
public IMultiPermissionCheck DoPreSaveSecurityCheck(int appId, IEnumerable <BundleWithHeader> items) { var app = Factory.Resolve <Apps.App>().Init(appId, Log, Block); var permCheck = new MultiPermissionsTypes().Init(Block.Context, app, items.Select(i => i.Header).ToList(), Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } if (!permCheck.UserCanWriteAndPublicFormsEnabled(out _, out error)) { throw HttpException.PermissionDenied(error); } Log.Add("passed security checks"); return(permCheck); }
// New feature in 11.03 - Usage Statistics public dynamic Usage(int appId, Guid guid) { var context = _ctxResolver.App(appId); var permCheck = ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } var item = context.AppState.List.One(guid); var relationships = item.Relationships.AllRelationships; // var result = relationships.Select(r => new EntityInRelationDto(r.)) // todo: don't forget Metadata relationships return(null); }
/// <summary> /// Initializes the object and performs all the initial security checks /// </summary> public virtual AdamContext Init(IContextOfApp context, string contentType, string fieldName, Guid entityGuid, bool usePortalRoot, ILog parentLog) { Log.LinkTo(parentLog); var appId = context.AppState.AppId; var callLog = Log.Call <AdamContext>($"app: {context.AppState.Show()}, field:{fieldName}, guid:{entityGuid}"); Context = context; Permissions = ServiceProvider.Build <MultiPermissionsTypes>() .Init(context, context.AppState, contentType, Log); // only do checks on field/guid if it's actually accessing that, if it's on the portal root, don't. UseSiteRoot = usePortalRoot; if (!usePortalRoot) { ItemField = fieldName; ItemGuid = entityGuid; } Security = ServiceProvider.Build <AdamSecurityChecksBase>().Init(this, usePortalRoot, Log); if (Security.MustThrowIfAccessingRootButNotAllowed(usePortalRoot, out var exception)) { throw exception; } Log.Add("check if feature enabled"); if (Security.UserIsRestricted && !Eav.Configuration.Features.Enabled(FeaturesForRestrictedUsers)) { throw HttpException.PermissionDenied( $"low-permission users may not access this - {Eav.Configuration.Features.MsgMissingSome(FeaturesForRestrictedUsers)}"); } if (string.IsNullOrEmpty(contentType) || string.IsNullOrEmpty(fieldName)) { return(callLog(null, this)); } Attribute = AttributeDefinition(appId, contentType, fieldName); if (!Security.FileTypeIsOkForThisField(out var exp)) { throw exp; } return(callLog(null, this)); }
public IEnumerable <EntityForPickerDto> GetAvailableEntities(IInstanceContext ctx, int appId, string[] items, string contentTypeName, int?dimensionId) { // do security check var permCheck = string.IsNullOrEmpty(contentTypeName) ? new MultiPermissionsApp().Init(ctx, GetApp(appId, null), Log) : new MultiPermissionsTypes().Init(ctx, GetApp(appId, null), contentTypeName, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } // maybe in the future, ATM not relevant var withDrafts = permCheck.EnsureAny(GrantSets.ReadDraft); return(new Eav.WebApi.EntityPickerApi(Log) .GetAvailableEntities(appId, items, contentTypeName, withDrafts, dimensionId)); }
public IEnumerable <EntityForPickerDto> GetAvailableEntities(int appId, string[] items, string contentTypeName) { var context = _ctxResolver.App(appId); // do security check var permCheck = string.IsNullOrEmpty(contentTypeName) ? ServiceProvider.Build <MultiPermissionsApp>().Init(context, context.AppState, Log) : ServiceProvider.Build <MultiPermissionsTypes>().Init(context, context.AppState, contentTypeName, Log); if (!permCheck.EnsureAll(GrantSets.ReadSomething, out var error)) { throw HttpException.PermissionDenied(error); } // maybe in the future, ATM not relevant var withDrafts = permCheck.EnsureAny(GrantSets.ReadDraft); return(_entityPickerApi.Init(Log).GetAvailableEntities(appId, items, contentTypeName, withDrafts)); }
public IMultiPermissionCheck DoPreSaveSecurityCheck(int appId, IEnumerable <BundleWithHeader> items) { var sp = Context.ServiceProvider; var app = sp.Build <Apps.App>().Init(sp, appId, Log, Context.UserMayEdit); var permCheck = sp.Build <MultiPermissionsTypes>().Init(Context, app, items.Select(i => i.Header).ToList(), Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } if (!permCheck.UserCanWriteAndPublicFormsEnabled(out _, out error)) { throw HttpException.PermissionDenied(error); } Log.Add("passed security checks"); return(permCheck); }
internal void Delete(IInstanceContext context, IBlock ctxBlock, string contentType, Guid guid, string appPath) { Log.Add($"delete guid:{guid}, type:{contentType}, path:{appPath}"); // if app-path specified, use that app, otherwise use from context var appIdentity = AppFinder.GetAppIdFromPathOrContext(appPath, ctxBlock); var entityApi = new EntityApi(appIdentity.AppId, true, Log); var itm = entityApi.GetOrThrow(contentType == "any" ? null : contentType, guid); var permCheck = new MultiPermissionsItems().Init(context, GetApp(appIdentity.AppId, ctxBlock), itm, Log); if (!permCheck.EnsureAll(Grants.Delete.AsSet(), out var error)) { throw HttpException.PermissionDenied(error); } entityApi.Delete(itm.Type.Name, guid); }
protected void VerifySecurityAndStructure(Eav.Apps.Assets.Folder <TFolderId, TFileId> parentFolder, IAssetWithParentSysId <TFolderId> target, string errPrefix) { // In case the primary file system is used (usePortalRoot) then also check higher permissions if (AdamContext.UseSiteRoot && !AdamContext.Security.CanEditFolder(target)) { throw HttpException.PermissionDenied(errPrefix + " - permission denied"); } if (!AdamContext.Security.SuperUserOrAccessingItemFolder(target.Path, out var exp)) { throw exp; } if (!EqualityComparer <TFolderId> .Default.Equals(target.ParentSysId, parentFolder.SysId)) { throw HttpException.BadRequest(errPrefix + " - not found in folder"); } }
/// <summary> /// Initializes the object and performs all the initial security checks /// </summary> protected AdamState(IBlock block, int appId, string contentType, string field, Guid guid, bool usePortalRoot, ILog log) : base("Adm.State", log) { var callLog = Log.Call($"field:{field}, guid:{guid}"); App = Factory.Resolve <Apps.App>().Init(appId, log, block); Permissions = new MultiPermissionsTypes() .Init(block.Context, App, contentType, Log); Block = block; // only do checks on field/guid if it's actually accessing that, if it's on the portal root, don't. UseTenantRoot = usePortalRoot; if (!usePortalRoot) { ItemField = field; ItemGuid = guid; } Security = Factory.Resolve <SecurityChecksBase>().Init(this, usePortalRoot, Log); SecurityCheckHelpers.ThrowIfAccessingRootButNotAllowed(usePortalRoot, Security.UserIsRestricted); Log.Add("check if feature enabled"); if (Security.UserIsRestricted && !ToSic.Eav.Configuration.Features.Enabled(FeaturesForRestrictedUsers)) { throw HttpException.PermissionDenied( $"low-permission users may not access this - {ToSic.Eav.Configuration.Features.MsgMissingSome(FeaturesForRestrictedUsers)}"); } PrepCore(App, guid, field, usePortalRoot); if (string.IsNullOrEmpty(contentType) || string.IsNullOrEmpty(field)) { return; } Attribute = Definition(appId, contentType, field); if (!Security.FileTypeIsOkForThisField(out var exp)) { throw exp; } callLog(null); }
/// <summary> /// Returns true if user isn't restricted, or if the restricted user is accessing a draft item /// </summary> internal bool UserIsNotRestrictedOrItemIsDraft(Guid guid, out HttpExceptionAbstraction exp) { Log.Add($"check if user is restricted ({UserIsRestricted}) or if the item '{guid}' is draft"); exp = null; // check that if the user should only see drafts, he doesn't see items of normal data if (!UserIsRestricted || FieldPermissionOk(GrantSets.ReadPublished)) { return(true); } // check if the data is public var itm = AdamState.AppRuntime.Entities.Get(guid); if (!(itm?.IsPublished ?? false)) { return(true); } exp = HttpException.PermissionDenied(Log.Add("user is restricted and may not see published, but item exists and is published - not allowed")); return(false); }
public EditDto Load(int appId, List <ItemIdentifier> items) { // Security check var wrapLog = Log.Call($"load many a#{appId}, items⋮{items.Count}"); var context = _ctxResolver.App(appId); var showDrafts = context.UserMayEdit; // do early permission check - but at this time it may be that we don't have the types yet // because they may be group/id combinations, without type information which we'll look up afterwards var appIdentity = State.Identity(null, appId); items = _contentGroupList.Init(appIdentity, Log, showDrafts).ConvertListIndexToId(items); TryToAutoFindMetadataSingleton(items, context.AppState); // now look up the types, and repeat security check with type-names // todo: 2020-03-20 new feat 11.01, may not check inner type permissions ATM var permCheck = ServiceProvider.Build <MultiPermissionsTypes>().Init(context, context.AppState, items, Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } // load items - similar var result = new EditDto(); var entityApi = _entityApi.Init(appId, permCheck.EnsureAny(GrantSets.ReadDraft), Log); var typeRead = entityApi.AppRead.ContentTypes; var list = entityApi.GetEntitiesForEditing(items); var jsonSerializer = ServiceProvider.Build <JsonSerializer>(); result.Items = list.Select(e => new BundleWithHeader <JsonEntity> { Header = e.Header, Entity = GetSerializeAndMdAssignJsonEntity(appId, e, jsonSerializer, typeRead) }).ToList(); // set published if some data already exists if (list.Any()) { result.IsPublished = list.First().Entity?.IsPublished ?? true; // Entity could be null (new), then true // only set draft-should-branch if this draft already has a published item if (!result.IsPublished) { result.DraftShouldBranch = list.First().Entity?.GetPublished() != null; } } // since we're retrieving data - make sure we're allowed to // this is to ensure that if public forms only have create permissions, they can't access existing data // important, this code is shared/duplicated in the EntitiesController.GetManyForEditing if (list.Any(set => set.Entity != null)) { if (!permCheck.EnsureAll(GrantSets.ReadSomething, out error)) { throw HttpException.PermissionDenied(error); } } // load content-types var types = UsedTypes(list, typeRead); result.ContentTypes = types .Select(ct => JsonSerializer.ToJson(ct, true)) .ToList(); //// TEMP DEBUG //var inputTypes = types.SelectMany(t => t.Attributes.Select(a => //{ // var Lookup = a.InputType(); // var mdAll = a.Metadata.Where(md => md.Type.Is(Constants.MetadataFieldTypeAll)).ToList(); // var MdAllCount = mdAll.Count; // var MdAllAttribCount = mdAll.FirstOrDefault()?.Attributes.Count; // var MdAllWithAttrib = mdAll.FirstOrDefault(md => md.Attributes.ContainsKey(Constants.MetadataFieldTypeAll)); // var MdAllAttrib = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll); // var MdAllAttribZero = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new string[0]); // var MdAllAttribEmpty = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new []{""}); // var MdAllAttribEn = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new[] { "en-us" }); // var MdAllAttribTr = MdAllWithAttrib?.GetBestValue<string>(Constants.MetadataFieldTypeAll, new[] { "tr-tr" }); // var MdAllType = a.Metadata.GetBestValue<string>(Constants.MetadataFieldAllInputType, Constants.MetadataFieldTypeAll); // return new {Lookup, MdAllCount, MdAllType, MdAllAttribCount, MdAllWithAttrib?.EntityId, MdAllAttrib, MdAllAttribZero, MdAllAttribEmpty, MdAllAttribEn, MdAllAttribTr }; //})); //var serializedDebug = JsonConvert.SerializeObject(inputTypes); //Log.Add("Test / debug: " + serializedDebug); // ensure that sub-properties of the content-types are included // this is for UI Formulas (children of @All) - WIP // and the warning/error Regex specials - WIP var entList = types.SelectMany( // in all Content-Type attributes like title, body etc. t => t.Attributes.SelectMany( // check all metadata of these attributes - get possible sub-entities attached a => a.Metadata.SelectMany(m => m.Children()) ) ); result.ContentTypeItems = entList.Select(e => jsonSerializer.ToJson(e, 0, Log)).ToList(); // Fix not-supported input-type names; map to correct name result.ContentTypes .ForEach(jt => jt.Attributes .ForEach(at => at.InputType = InputTypes.MapInputTypeV10(at.InputType))); // load input-field configurations result.InputTypes = GetNecessaryInputTypes(result.ContentTypes, typeRead); // also include UI features result.Features = FeaturesHelpers.FeatureListWithPermissionCheck(permCheck).ToList(); // Attach context, but only the minimum needed for the UI result.Context = _contextBuilder.SetZoneAndApp(appIdentity.ZoneId, context.AppState) .Get(Ctx.AppBasic | Ctx.Language | Ctx.Site | Ctx.System); try { result.Prefetch = TryToPrefectAdditionalData(appId, result); } catch (Exception) { /* ignore */ } // done - return wrapLog($"ready, sending items:{result.Items.Count}, " + $"types:{result.ContentTypes.Count}, " + $"inputs:{result.InputTypes.Count}, " + $"feats:{result.Features.Count}"); return(result); }
public Dictionary <Guid, int> Save(EditDto package, bool partOfPage) { Log.Add($"save started with a#{_appId}, i⋮{package.Items.Count}, partOfPage:{partOfPage}"); var validator = new SaveDataValidator(package, Log); // perform some basic validation checks if (!validator.ContainsOnlyExpectedNodes(out var exp)) { throw exp; } // todo: unsure about this - thought I should check contentblockappid in group-header, because this is where it should be saved! //var contextAppId = appId; //var targetAppId = package.Items.First().Header.Group.ContentBlockAppId; //if (targetAppId != 0) //{ // Log.Add($"detected content-block app to use: {targetAppId}; in context of app {contextAppId}"); // appId = targetAppId; //} var appMan = _appManagerLazy.Value.Init(_appId, Log); var appRead = appMan.Read; var ser = ServiceProvider.Build <JsonSerializer>().Init(appRead.AppState, Log); // Since we're importing directly into this app, we would prefer local content-types ser.PreferLocalAppTypes = true; validator.PrepareForEntityChecks(appRead); #region check if it's an update, and do more security checks then - shared with EntitiesController.Save // basic permission checks var permCheck = new Save.SaveSecurity(_context, Log) .DoPreSaveSecurityCheck(_appId, package.Items); var foundItems = package.Items.Where(i => i.Entity.Id != 0 && i.Entity.Guid != Guid.Empty) .Select(i => i.Entity.Guid != Guid.Empty ? appRead.Entities.Get(i.Entity.Guid) // prefer guid access if available : appRead.Entities.Get(i.Entity.Id) // otherwise id ); if (foundItems.Any(i => i != null) && !permCheck.EnsureAll(GrantSets.UpdateSomething, out var error)) { throw HttpException.PermissionDenied(error); } #endregion var items = package.Items.Select(i => { var ent = ser.Deserialize(i.Entity, false, false) as Entity; var index = package.Items.IndexOf(i); // index is helpful in case of errors if (!validator.EntityIsOk(index, ent, out exp)) { throw exp; } if (!validator.IfUpdateValidateAndCorrectIds(index, ent, out exp)) { throw exp; } ent.IsPublished = package.IsPublished; ent.PlaceDraftInBranch = package.DraftShouldBranch; // new in 11.01 if (i.Header.ListHas()) { // Check if Add was true, and fix if it had already been saved (EntityId != 0) // the entityId is reset by the validator if it turns out to be an update // todo: verify use - maybe it's to set before we save, as maybe afterwards it's always != 0? var add = i.Header.ListAdd(); i.Header.Add = add; if (ent.EntityId > 0 && add) { i.Header.Add = false; } } return(new BundleWithHeader <IEntity> { Header = i.Header, Entity = ent }); }) .ToList(); Log.Add("items to save generated, all data tests passed"); return(_pagePublishing.SaveInPagePublishing(_ctxResolver.RealBlockOrNull(), _appId, items, partOfPage, forceSaveAsDraft => DoSave(appMan, items, forceSaveAsDraft), permCheck)); }
public AllInOneDto Load(IBlock block, IContextBuilder contextBuilder, int appId, List <ItemIdentifier> items) { // Security check var wraplog = Log.Call($"load many a#{appId}, items⋮{items.Count}"); // do early permission check - but at this time it may be that we don't have the types yet // because they may be group/id combinations, without type information which we'll look up afterwards var appIdentity = State.Identity(null, appId); //var block = GetBlock(); items = new ContentGroupList(block, Log).ConvertListIndexToId(items, appIdentity); // now look up the types, and repeat security check with type-names // todo: 2020-03-20 new feat 11.01, may not check inner type permissions ATM var permCheck = new MultiPermissionsTypes().Init(block.Context, GetApp(appId, block), items, Log); if (!permCheck.EnsureAll(GrantSets.WriteSomething, out var error)) { throw HttpException.PermissionDenied(error); } // load items - similar var result = new AllInOneDto(); var entityApi = new EntityApi(appId, permCheck.EnsureAny(GrantSets.ReadDraft), Log); var typeRead = entityApi.AppRead.ContentTypes; var list = entityApi.GetEntitiesForEditing(items); var jsonSerializer = new JsonSerializer(); result.Items = list.Select(e => new BundleWithHeader <JsonEntity> { Header = e.Header, Entity = GetSerializeAndMdAssignJsonEntity(appId, e, jsonSerializer, typeRead) }).ToList(); // set published if some data already exists if (list.Any()) { result.IsPublished = list.First().Entity?.IsPublished ?? true; // Entity could be null (new), then true // only set draft-should-branch if this draft already has a published item if (!result.IsPublished) { result.DraftShouldBranch = list.First().Entity?.GetPublished() != null; } } // since we're retrieving data - make sure we're allowed to // this is to ensure that if public forms only have create permissions, they can't access existing data // important, this code is shared/duplicated in the EntitiesController.GetManyForEditing if (list.Any(set => set.Entity != null)) { if (!permCheck.EnsureAll(GrantSets.ReadSomething, out error)) { throw HttpException.PermissionDenied(error); } } // load content-types var types = UsedTypes(list, typeRead); result.ContentTypes = types .Select(ct => JsonSerializer.ToJson(ct, true)) .ToList(); // todo: ensure that sub-properties of the content-types are included var entList = types.SelectMany( // in all Content-Type attributes like title, body etc. t => t.Attributes.SelectMany( // check all metadata of these attributes - get possible sub-entities attached a => a.Metadata.SelectMany(m => m.Children()) ) ); result.ContentTypeItems = entList.Select(e => jsonSerializer.ToJson(e, 0, Log)).ToList(); // Fix not-supported input-type names; map to correct name result.ContentTypes .ForEach(jt => jt.Attributes .ForEach(at => at.InputType = InputTypes.MapInputTypeV10(at.InputType))); // load input-field configurations result.InputTypes = GetNecessaryInputTypes(result.ContentTypes, typeRead); // also include UI features result.Features = FeaturesHelpers.FeatureListWithPermissionCheck(permCheck).ToList(); // Attach context result.Context = contextBuilder.InitApp(appIdentity.ZoneId, permCheck.App) .Get(Ctx.AppBasic | Ctx.Language | Ctx.Site | Ctx.System); // done - return wraplog($"ready, sending items:{result.Items.Count}, " + $"types:{result.ContentTypes.Count}, " + $"inputs:{result.InputTypes.Count}, " + $"feats:{result.Features.Count}"); return(result); }
public IFile UploadOne(Stream stream, string originalFileName, string subFolder, bool skipFieldAndContentTypePermissionCheck) { Log.Add($"upload one subfold:{subFolder}, file: {originalFileName}"); // make sure the file name we'll use doesn't contain injected path-traversal originalFileName = Path.GetFileName(originalFileName); HttpExceptionAbstraction exp; if (!skipFieldAndContentTypePermissionCheck) { if (!State.Security.UserIsPermittedOnField(GrantSets.WriteSomething, out exp)) { throw exp; } // check that if the user should only see drafts, he doesn't see items of published data if (!State.Security.UserIsNotRestrictedOrItemIsDraft(State.ItemGuid, out var permissionException)) { throw permissionException; } } var folder = State.ContainerContext.Folder(); if (!string.IsNullOrEmpty(subFolder)) { folder = State.ContainerContext.Folder(subFolder, false); } // start with a security check... var fs = State.AdamAppContext.AdamFs; var parentFolder = fs.GetFolder(folder.SysId); // validate that dnn user have write permissions for folder in case dnn file system is used (usePortalRoot) if (State.UseSiteRoot && !State.Security.CanEditFolder(parentFolder)) { throw HttpException.PermissionDenied("can't upload - permission denied"); } // we only upload into valid adam if that's the scenario if (!State.Security.SuperUserOrAccessingItemFolder(parentFolder.Path, out exp)) { throw exp; } #region check content-type extensions... // Check file size and extension var fileName = string.Copy(originalFileName); if (!State.Security.ExtensionIsOk(fileName, out var exceptionAbstraction)) { throw exceptionAbstraction; } // check metadata of the FieldDef to see if still allowed extension // note 2018-04-20 2dm: can't do this for wysiwyg, as it doesn't have a setting for allowed file-uploads var additionalFilter = State.Attribute.Metadata.GetBestValue <string>("FileFilter"); if (!string.IsNullOrWhiteSpace(additionalFilter) && !new AdamSecurityCheckHelpers().Init(Log).CustomFileFilterOk(additionalFilter, fileName)) { throw HttpException.NotAllowedFileType(fileName, "field has custom file-filter, which doesn't match"); } #endregion var maxSizeKb = fs.MaxUploadKb(); if (stream.Length > 1024 * maxSizeKb) { throw new Exception($"file too large - more than {maxSizeKb}Kb"); } // remove forbidden / troubling file name characters fileName = fileName .Replace("+", "plus") .Replace("%", "per") .Replace("#", "hash"); if (fileName != originalFileName) { Log.Add($"cleaned file name from'{originalFileName}' to '{fileName}'"); } var eavFile = fs.Add(parentFolder, stream, fileName, true); return(eavFile); }