Example #1
0
        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);
        }
Example #2
0
        /// <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));
        }
Example #3
0
        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),
            });
        }
Example #5
0
        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));
        }
Example #6
0
 private void ThrowIfNotSuperuser()
 {
     if (!PortalSettings.UserInfo.IsSuperUser)
     {
         throw HttpException.PermissionDenied("requires Superuser permissions");
     }
 }
Example #7
0
        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),
            });
        }
Example #8
0
        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);
        }
Example #10
0
        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)));
        }
Example #11
0
 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);
 }
Example #12
0
 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));
 }
Example #13
0
        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);
        }
Example #14
0
        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));
        }
Example #15
0
 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);
 }
Example #16
0
        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);
        }
Example #17
0
        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);
        }
Example #18
0
        // 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);
        }
Example #19
0
        /// <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));
        }
Example #20
0
        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));
        }
Example #21
0
        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));
        }
Example #22
0
        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);
        }
Example #23
0
        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);
        }
Example #24
0
        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");
            }
        }
Example #25
0
        /// <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);
        }
Example #26
0
        /// <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);
        }
Example #27
0
        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);
        }
Example #28
0
        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));
        }
Example #29
0
        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);
        }
Example #30
0
        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);
        }