Beispiel #1
0
        public IEnumerable <AdamItem> Folder(int appId, string contentType, Guid guid, string field, string subfolder, string newFolder, bool usePortalRoot)
        {
            Log.Add($"get folders for a:{appId}, i:{guid}, field:{field}, subfld:{subfolder}, new:{newFolder}, useRoot:{usePortalRoot}");
            var state = new AdamSecureState(BlockBuilder, appId, contentType, field, guid, usePortalRoot, Log);

            if (state.UserIsRestricted && !state.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 = state.ContainerContext.Folder();

            // try to see if we can get into the subfolder - will throw error if missing
            if (!string.IsNullOrEmpty(subfolder))
            {
                folder = state.ContainerContext.Folder(subfolder, false);
            }

            // start with a security check...
            var dnnFolder = FolderManager.Instance.GetFolder(folder.Id);

            // validate that dnn user have write permissions for folder in case dnn file system is used (usePortalRoot)
            if (usePortalRoot && !SecurityChecks.CanEdit(dnnFolder))
            {
                throw Http.PermissionDenied("can't create new folder - permission denied");
            }

            var newFolderPath = string.IsNullOrEmpty(subfolder) ? newFolder : Path.Combine(subfolder, newFolder).Replace("\\", "/");

            // now access the subfolder, creating it if missing (which is what we want
            state.ContainerContext.Folder(newFolderPath, true);

            return(Items(appId, contentType, guid, field, subfolder, usePortalRoot));
        }
Beispiel #2
0
 internal AdamItem(IFolderInfo original, bool usePortalRoot, AdamSecureState state)
 {
     IsFolder  = true;
     Id        = original.FolderID;
     ParentId  = original.ParentID;
     Path      = original.DisplayPath;
     Name      = original.DisplayName;
     Size      = 0;
     Type      = "folder";
     Created   = original.CreatedOnDate;
     Modified  = original.LastModifiedOnDate;
     AllowEdit = usePortalRoot ? SecurityChecks.CanEdit(original) : !state.UserIsRestricted || state.FieldPermissionOk(GrantSets.WriteSomething);
 }
Beispiel #3
0
 internal AdamItem(IFileInfo original, bool usePortalRoot, AdamSecureState state)
 {
     IsFolder  = false;
     Id        = original.FileId;
     ParentId  = original.FolderId;
     Path      = original.RelativePath;
     Name      = original.FileName;
     Size      = original.Size;
     Type      = "unknown"; // will be set from the outside
     Created   = original.CreatedOnDate;
     Modified  = original.LastModifiedOnDate;
     AllowEdit = usePortalRoot ? SecurityChecks.CanEdit(original) : !state.UserIsRestricted || state.FieldPermissionOk(GrantSets.WriteSomething);
 }
Beispiel #4
0
        public bool ExtensionIsOk(string fileName, out HttpResponseException preparedException)
        {
            if (!SecurityChecks.IsAllowedDnnExtension(fileName))
            {
                preparedException = Http.NotAllowedFileType(fileName, "Not in whitelisted CMS file types.");
                return(false);
            }

            if (SecurityChecks.IsKnownRiskyExtension(fileName))
            {
                preparedException = Http.NotAllowedFileType(fileName, "This is a known risky file type.");
                return(false);
            }
            preparedException = null;
            return(true);
        }
Beispiel #5
0
        /// <summary>
        /// Initializes the object and performs all the initial security checks
        /// </summary>
        public AdamSecureState(IBlockBuilder blockBuilder, int appId, string contentType, string field, Guid guid, bool usePortalRoot, ILog log)
            : base(blockBuilder, appId, contentType, log)
        {
            // only do checks on field/guid if it's actually accessing that, if it's on the portal root, don't.
            if (!usePortalRoot)
            {
                Field = field;
                Guid  = guid;
            }

            var firstChecker          = PermissionCheckers.First().Value;
            var userMayAdminSomeFiles = firstChecker.UserMay(GrantSets.WritePublished);

            UserMayAdminSiteFiles = firstChecker.GrantedBecause == Conditions.EnvironmentGlobal ||
                                    firstChecker.GrantedBecause == Conditions.EnvironmentInstance;

            UserIsRestricted = !(usePortalRoot
                ? UserMayAdminSiteFiles
                : userMayAdminSomeFiles);


            Log.Add($"AdamSecureState - field:{field}, guid:{guid}, adminSome:{userMayAdminSomeFiles}, restricted:{UserIsRestricted}");

            SecurityChecks.ThrowIfAccessingRootButNotAllowed(usePortalRoot, UserIsRestricted);

            Log.Add("check if feature enabled");
            if (UserIsRestricted && !Feats.Enabled(FeaturesForRestrictedUsers))
            {
                throw Http.PermissionDenied(
                          $"low-permission users may not access this - {Feats.MsgMissingSome(FeaturesForRestrictedUsers)}");
            }

            PrepCore(App, guid, field, usePortalRoot);

            if (string.IsNullOrEmpty(contentType) || string.IsNullOrEmpty(field))
            {
                return;
            }

            Attribute = Definition(appId, contentType, field);
            if (!FileTypeIsOkForThisField(out var exp))
            {
                throw exp;
            }
        }
Beispiel #6
0
 public bool SuperUserOrAccessingItemFolder(string path, out HttpResponseException preparedException)
 {
     preparedException = null;
     return(!UserIsRestricted || SecurityChecks.DestinationIsInItem(Guid, Field, path, out preparedException));
 }
Beispiel #7
0
        public bool Rename(int appId, string contentType, Guid guid, string field, string subfolder, bool isFolder, int id, string newName, bool usePortalRoot)
        {
            Log.Add($"rename a:{appId}, i:{guid}, field:{field}, subf:{subfolder}, isfld:{isFolder}, new:{newName}, useRoot:{usePortalRoot}");

            var state = new AdamSecureState(BlockBuilder, appId, contentType, field, guid, usePortalRoot, Log);

            if (!state.UserIsPermittedOnField(GrantSets.WriteSomething, out var exp))
            {
                throw exp;
            }

            // check that if the user should only see drafts, he doesn't see items of published data
            if (!state.UserIsNotRestrictedOrItemIsDraft(guid, out var permissionException))
            {
                throw permissionException;
            }

            // try to see if we can get into the subfolder - will throw error if missing
            var current = state.ContainerContext.Folder(subfolder, false);

            if (isFolder)
            {
                var folderManager = FolderManager.Instance;
                var fld           = folderManager.GetFolder(id);

                // validate that dnn user have write permissions for folder in case dnn file system is used (usePortalRoot)
                if (usePortalRoot && !SecurityChecks.CanEdit(fld))
                {
                    throw Http.PermissionDenied("can't rename folder - permission denied");
                }

                if (!state.SuperUserOrAccessingItemFolder(fld.PhysicalPath, out exp))
                {
                    throw exp;
                }

                if (fld.ParentID != current.Id)
                {
                    throw Http.BadRequest("can't rename folder - not found in folder");
                }
                folderManager.RenameFolder(fld, newName);
            }
            else
            {
                var fileManager = FileManager.Instance;
                var file        = fileManager.GetFile(id);

                // validate that dnn user have write permissions for folder where is file in case dnn file system is used (usePortalRoot)
                if (usePortalRoot && !SecurityChecks.CanEdit(file))
                {
                    throw Http.PermissionDenied("can't rename file - permission denied");
                }

                if (!state.SuperUserOrAccessingItemFolder(file.PhysicalPath, out exp))
                {
                    throw exp;
                }

                if (file.FolderId != current.Id)
                {
                    throw Http.BadRequest("can't rename file - not found in folder");
                }

                // never allow to change the extension
                if (file.Extension != newName.Split('.').Last())
                {
                    newName += "." + file.Extension;
                }
                fileManager.RenameFile(file, newName);
            }

            Log.Add("rename complete");
            return(true);
        }
Beispiel #8
0
        public IFile UploadOne(Stream stream, string originalFileName, string contentType, Guid guid, string field, string subFolder, bool usePortalRoot, bool skipFieldAndContentTypePermissionCheck)
        {
            Log.Add($"upload one a:{_appId}, i:{guid}, field:{field}, subfold:{subFolder}, useRoot:{usePortalRoot}");

            var state = new AdamSecureState(BlockBuilder, _appId, contentType, field, guid, usePortalRoot, Log);
            HttpResponseException exp;

            if (!skipFieldAndContentTypePermissionCheck)
            {
                if (!state.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.UserIsNotRestrictedOrItemIsDraft(guid, 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 dnnFolder = FolderManager.Instance.GetFolder(folder.Id);

            // validate that dnn user have write permissions for folder in case dnn file system is used (usePortalRoot)
            if (usePortalRoot && !SecurityChecks.CanEdit(dnnFolder))
            {
                throw Http.PermissionDenied("can't upload - permission denied");
            }

            // we only upload into valid adam if that's the scenario
            if (!state.SuperUserOrAccessingItemFolder(dnnFolder.PhysicalPath, out exp))
            {
                throw exp;
            }

            #region check content-type extensions...

            // Check file size and extension
            var fileName = String.Copy(originalFileName);
            if (!state.ExtensionIsOk(fileName, out exp))
            {
                throw exp;
            }

            // check metadata of the FieldDef to see if still allowed extension
            var additionalFilter = state.Attribute.Metadata.GetBestValue <string>("FileFilter");
            if (!string.IsNullOrWhiteSpace(additionalFilter) &&
                !state.CustomFileFilterOk(additionalFilter, originalFileName))
            {
                throw Http.NotAllowedFileType(fileName, "field has custom file-filter, which doesn't match");
            }

            // note 2018-04-20 2dm: can't do this for wysiwyg, as it doesn't have a setting for allowed file-uploads

            #endregion

            if (stream.Length > 1024 * MaxFileSizeKb)
            {
                throw new Exception($"file too large - more than {MaxFileSizeKb}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}'");
            }

            // Make sure the image does not exist yet, cycle through numbers (change file name)
            fileName = RenameFileToNotOverwriteExisting(fileName, dnnFolder);

            // Everything is ok, add file to DNN
            var dnnFile = FileManager.Instance.AddFile(dnnFolder, Path.GetFileName(fileName),
                                                       stream);

            var adamcontext = new AdamAppContext(BlockBuilder.Block.Tenant, state.App, BlockBuilder, 10, Log);
            var eavFile     = new File(adamcontext)
            {
                Created   = dnnFile.CreatedOnDate,
                Extension = dnnFile.Extension,
                FullName  = dnnFile.FileName,
                Folder    = dnnFile.Folder,
                FolderId  = dnnFile.FolderId,
                Id        = dnnFile.FileId,
                Modified  = dnnFile.LastModificationTime
            };

            return(eavFile);
        }