Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File extension whitelists for end users #3633

Merged
merged 32 commits into from
Apr 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
38eb895
WIP
donker Mar 1, 2020
f540909
WIP
donker Mar 3, 2020
d1297a4
WIP
donker Mar 4, 2020
aca4eef
Remove images stuff for now. It doesn't look like this would work.
donker Mar 4, 2020
3ed091b
Clean
donker Mar 4, 2020
ad1e292
Set template and upgrade for host setting
donker Mar 4, 2020
df3a657
Recheck all portals after the host has changed the value for extensio…
donker Mar 4, 2020
b01489a
Fix
donker Mar 4, 2020
28ff4bb
UI for new host setting
donker Mar 4, 2020
463a60b
Editing of the portal setting for whitelist
donker Mar 4, 2020
13edbeb
Fix
donker Mar 5, 2020
964ab8d
Fixes for lack of context during install
donker Mar 5, 2020
258d33d
Update DNN Platform/Library/Data/DataProvider.cs
donker Mar 19, 2020
05e8a2c
Update DNN Platform/Library/Common/Utilities/FileExtensionWhitelist.cs
donker Mar 19, 2020
8814aea
WIP
donker Mar 1, 2020
fc989e1
WIP
donker Mar 3, 2020
7f8398a
WIP
donker Mar 4, 2020
a1c28b0
Remove images stuff for now. It doesn't look like this would work.
donker Mar 4, 2020
0aa00e1
Clean
donker Mar 4, 2020
4d1c4ec
Set template and upgrade for host setting
donker Mar 4, 2020
eacb60a
Recheck all portals after the host has changed the value for extensio…
donker Mar 4, 2020
8564553
Fix
donker Mar 4, 2020
74be7e4
UI for new host setting
donker Mar 4, 2020
d4699a8
Editing of the portal setting for whitelist
donker Mar 4, 2020
2a3710d
Fix
donker Mar 5, 2020
9bb4b8a
Fixes for lack of context during install
donker Mar 5, 2020
86bb904
Update DNN Platform/Library/Data/DataProvider.cs
donker Mar 19, 2020
c7cc924
Update DNN Platform/Library/Common/Utilities/FileExtensionWhitelist.cs
donker Mar 19, 2020
ba549cb
Improvements to code based on suggestions
donker Apr 9, 2020
d6e2704
Uninstall provider
donker Apr 9, 2020
e1a956c
Ensure default end users whitelist is there during upgrade and for te…
donker Apr 11, 2020
75de3e2
Remove double entry from project
donker Apr 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public Task<HttpResponseMessage> PostFile()
// local references for use in closure
var portalSettings = PortalSettings;
var currentSynchronizationContext = SynchronizationContext.Current;
var userInfo = UserInfo;
var userInfo = UserInfo;
var task = request.Content.ReadAsMultipartAsync(provider)
.ContinueWith(o =>
{
Expand Down Expand Up @@ -420,7 +420,7 @@ private static FileUploadDto UploadFile(
}

var folderManager = FolderManager.Instance;
var effectivePortalId = isHostPortal ? Null.NullInteger : portalId;
var effectivePortalId = isHostPortal ? Null.NullInteger : portalId;
var folderInfo = folderManager.GetFolder(effectivePortalId, folder);

int userId;
Expand Down
10 changes: 8 additions & 2 deletions DNN Platform/Library/Common/Utilities/FileExtensionWhitelist.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ public string ToStorageString(IEnumerable<string> additionalExtensions)

private IEnumerable<string> CombineLists(IEnumerable<string> additionalExtensions)
{
if(additionalExtensions == null)
if (additionalExtensions == null)
{
return _extensions;
}

//toList required to ensure that multiple enumerations of the list are possible
var additionalExtensionsList = additionalExtensions.ToList();
if( !additionalExtensionsList.Any())
if (!additionalExtensionsList.Any())
{
return _extensions;
}
Expand All @@ -141,5 +141,11 @@ private IEnumerable<string> NormalizeExtensions(IEnumerable<string> additionalEx
{
return additionalExtensions.Select(ext => (ext.StartsWith(".") ? ext : "." + ext).ToLowerInvariant());
}

public FileExtensionWhitelist RestrictBy(FileExtensionWhitelist parentList)
{
var filter = parentList._extensions;
return new FileExtensionWhitelist(string.Join(",", _extensions.Where(x => filter.Contains(x)).Select(s => s.Substring(1))));
}
}
}
13 changes: 13 additions & 0 deletions DNN Platform/Library/Data/DataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,19 @@ public virtual IDataReader GetPortalSettings(int PortalId, string CultureCode)
return ExecuteReader("GetPortalSettings", PortalId, CultureCode);
}

internal virtual IDictionary<int, string> GetPortalSettingsBySetting(string settingName, string cultureCode)
{
var result = new Dictionary<int, string>();
using (var reader = ExecuteReader("GetPortalSettingsBySetting", settingName, cultureCode))
{
while (reader.Read())
{
result[reader.GetInt32(0)] = reader.GetString(1);
}
}
return result;
}

public virtual IDataReader GetPortalSpaceUsed(int PortalId)
{
return ExecuteReader("GetPortalSpaceUsed", GetNull(PortalId));
Expand Down
13 changes: 12 additions & 1 deletion DNN Platform/Library/Entities/Host/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,18 @@ public static FileExtensionWhitelist AllowedExtensionWhitelist
return new FileExtensionWhitelist(HostController.Instance.GetString("FileExtensions"));
}
}


/// <summary>
/// Default list of extensions an end user is allowed to upload.
/// </summary>
public static FileExtensionWhitelist DefaultEndUserExtensionWhitelist
{
get
{
return new FileExtensionWhitelist(HostController.Instance.GetString("DefaultEndUserExtensionWhitelist"));
}
}

/// -----------------------------------------------------------------------------
/// <summary>
/// Gets the GUID
Expand Down
4 changes: 4 additions & 0 deletions DNN Platform/Library/Entities/Portals/PortalSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,10 @@ public bool DisablePrivateMessage
/// </summary>
public string DataConsentDelayMeasurement { get; internal set; }

/// <summary>
/// Whitelist of file extensions for end users
/// </summary>
public FileExtensionWhitelist AllowedExtensionsWhitelist { get; internal set; }
#endregion

#region IPropertyAccess Members
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ public virtual void LoadPortalSettings(PortalSettings portalSettings)
portalSettings.DataConsentDelay = int.Parse(setting);
setting = settings.GetValueOrDefault("DataConsentDelayMeasurement", "d");
portalSettings.DataConsentDelayMeasurement = setting;

setting = settings.GetValueOrDefault("AllowedExtensionsWhitelist", HostController.Instance.GetString("DefaultEndUserExtensionWhitelist"));
portalSettings.AllowedExtensionsWhitelist = new FileExtensionWhitelist(setting);
}

protected virtual void UpdateSkinSettings(TabInfo activeTab, PortalSettings portalSettings)
Expand Down
19 changes: 18 additions & 1 deletion DNN Platform/Library/Entities/Users/UserInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ public string FirstName
[Browsable(false)]
public bool IsSuperUser { get; set; }


/// <summary>
/// Gets whether the user is in the portal's administrators role
/// </summary>
public bool IsAdmin
bdukes marked this conversation as resolved.
Show resolved Hide resolved
{
get
{
if (IsSuperUser)
{
return true;
}
PortalInfo ps = PortalController.Instance.GetPortal(PortalID);
return ps != null && IsInRole(ps.AdministratorRoleName);
}
}

/// -----------------------------------------------------------------------------
/// <summary>
/// Gets and sets the Last IP address used by user
Expand Down Expand Up @@ -238,7 +255,7 @@ public string[] Roles
(r.EffectiveDate < DateTime.Now || Null.IsNull(r.EffectiveDate)) &&
(r.ExpiryDate > DateTime.Now || Null.IsNull(r.ExpiryDate))
select r.RoleName
).ToArray();
).ToArray();
}
set { }
}
Expand Down
101 changes: 63 additions & 38 deletions DNN Platform/Library/Security/PortalSecurity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public class PortalSecurity
new Regex("alert[\\s(&nbsp;)]*\\([\\s(&nbsp;)]*'?[\\s(&nbsp;)]*[\"(&quot;)]?", RxOptions),
new Regex(@"eval*.\(", RxOptions),
};

private static readonly Regex DangerElementsRegex = new Regex(@"(<[^>]*?) on.*?\=(['""]*)[\s\S]*?(\2)( *)([^>]*?>)", RxOptions);
private static readonly Regex DangerElementContentRegex = new Regex(@"on.*?\=(['""]*)[\s\S]*?(\1)( *)", RxOptions);

Expand All @@ -110,7 +110,7 @@ public enum FilterFlag
NoScripting = 4,
NoSQL = 8,
NoAngleBrackets = 16,
NoProfanity =32
NoProfanity = 32
}

/// <summary>
Expand Down Expand Up @@ -236,7 +236,7 @@ private static void ProcessSecurityRole(UserInfo user, PortalSettings settings,
roleAllowed = true;
}
}
}
}
}

private static RoleType GetRoleType(string roleName)
Expand Down Expand Up @@ -280,7 +280,7 @@ private static string BytesToHexString(IEnumerable<byte> bytes)
///-----------------------------------------------------------------------------
private static string FilterStrings(string strInput)
{
//setup up list of search terms as items may be used twice
//setup up list of search terms as items may be used twice
var tempInput = strInput;
if (string.IsNullOrEmpty(tempInput))
{
Expand All @@ -301,7 +301,7 @@ private static string FilterStrings(string strInput)
//check if text contains encoded angle brackets, if it does it we decode it to check the plain text
if (tempInput.Contains("&gt;") || tempInput.Contains("&lt;"))
{
//text is encoded, so decode and try again
//text is encoded, so decode and try again
tempInput = HttpUtility.HtmlDecode(tempInput);
tempInput = RxListStrings.Aggregate(tempInput, (current, s) => s.Replace(current, replacement));

Expand Down Expand Up @@ -399,10 +399,10 @@ private static bool IncludesMarkup(string strInput)
{
return StripTagsRegex.IsMatch(strInput);
}
#endregion
#region Public Methods

#endregion

#region Public Methods

///-----------------------------------------------------------------------------
/// <summary>
Expand Down Expand Up @@ -574,14 +574,14 @@ public string Remove(string inputString, ConfigType configType, string configSou
case ConfigType.ListController:
const RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
const string listName = "ProfanityFilter";

var listController = new ListController();

PortalSettings settings;

IEnumerable<ListEntryInfo> listEntryHostInfos;
IEnumerable<ListEntryInfo> listEntryPortalInfos;

switch (filterScope)
{
case FilterScope.SystemList:
Expand All @@ -598,7 +598,7 @@ public string Remove(string inputString, ConfigType configType, string configSou
case FilterScope.PortalList:
settings = PortalController.Instance.GetCurrentPortalSettings();
listEntryPortalInfos = listController.GetListEntryInfoItems(listName + "-" + settings.PortalId, "", settings.PortalId);
inputString = listEntryPortalInfos.Aggregate(inputString, (current, removeItem) => Regex.Replace(current, @"\b" + Regex.Escape(removeItem.Text) + @"\b", string.Empty, options));
inputString = listEntryPortalInfos.Aggregate(inputString, (current, removeItem) => Regex.Replace(current, @"\b" + Regex.Escape(removeItem.Text) + @"\b", string.Empty, options));
break;
}

Expand Down Expand Up @@ -628,12 +628,12 @@ public void SignIn(UserInfo user, bool createPersistentCookie)

//Create a new Cookie
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedAuthTicket)
{
Expires = authenticationTicket.Expiration,
Domain = GetCookieDomain(user.PortalID),
Path = FormsAuthentication.FormsCookiePath,
Secure = FormsAuthentication.RequireSSL
};
{
Expires = authenticationTicket.Expiration,
Domain = GetCookieDomain(user.PortalID),
Path = FormsAuthentication.FormsCookiePath,
Secure = FormsAuthentication.RequireSSL
};

if (HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName] != null)
HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
Expand Down Expand Up @@ -734,7 +734,7 @@ public void SignOut()
}

//Remove current userinfo from context items
HttpContext.Current.Items.Remove("UserInfo");
HttpContext.Current.Items.Remove("UserInfo");

//remove language cookie
var httpCookie = HttpContext.Current.Response.Cookies["language"];
Expand All @@ -755,15 +755,15 @@ public void SignOut()
if (cookie != null)
{
cookie.Value = null;
cookie.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
cookie.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
cookie.Expires = DateTime.Now.AddYears(-30);
}

cookie = HttpContext.Current.Response.Cookies["portalroles"];
if (cookie != null)
{
cookie.Value = null;
cookie.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
cookie.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
cookie.Expires = DateTime.Now.AddYears(-30);
}

Expand All @@ -777,7 +777,7 @@ public void SignOut()
if (auth != null)
{
auth.Value = null;
auth.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
auth.Path = (!string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/");
auth.Expires = DateTime.Now.AddYears(-30);
}
}
Expand Down Expand Up @@ -809,19 +809,44 @@ public bool ValidateInput(string userInput, FilterFlag filterType)

return (userInput == filteredInput);
}

#endregion

#region Public Shared/Static Methods

/// <summary>
/// This function loops through every portal that has set its own AllowedExtensionWhitelist
/// and checks that there are no extensions there that are restriced by the host
///
/// The only time we should call this is if the host allowed extensions list has changed
/// </summary>
/// <param name="newMasterList">Comma separated list of extensions that govern all users on this installation</param>
public void CheckAllPortalFileExtensionWhitelists(string newMasterList)
{
var masterList = new FileExtensionWhitelist(newMasterList);
var portalSettings = Data.DataProvider.Instance().GetPortalSettingsBySetting("AllowedExtensionsWhitelist", null);
foreach (var portalId in portalSettings.Keys)
{
if (!string.IsNullOrEmpty(portalSettings[portalId]))
{
var portalExts = new FileExtensionWhitelist(portalSettings[portalId]);
var newValue = portalExts.RestrictBy(masterList).ToStorageString();
if (newValue != portalSettings[portalId])
{
PortalController.UpdatePortalSetting(portalId, "AllowedExtensionsWhitelist", newValue, false);
}
}
}
}

#endregion

#region Public Shared/Static Methods

public static void ForceSecureConnection()
{
//get current url
//get current url
var url = HttpContext.Current.Request.Url.ToString();
//if unsecure connection
//if unsecure connection
if (url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
{
//switch to secure connection
//switch to secure connection
url = "https://" + url.Substring("http://".Length);
//append ssl parameter to querystring to indicate secure connection processing has already occurred
if (url.IndexOf("?", StringComparison.Ordinal) == -1)
Expand All @@ -846,9 +871,9 @@ public static string GetCookieDomain(int portalId)
var groupController = new PortalGroupController();
var group = groupController.GetPortalGroups().SingleOrDefault(p => p.MasterPortalId == PortalController.GetEffectivePortalId(portalId));

if (@group != null
&& !string.IsNullOrEmpty(@group.AuthenticationDomain)
&& PortalSettings.Current.PortalAlias.HTTPAlias.Contains(@group.AuthenticationDomain))
if (@group != null
&& !string.IsNullOrEmpty(@group.AuthenticationDomain)
&& PortalSettings.Current.PortalAlias.HTTPAlias.Contains(@group.AuthenticationDomain))
{
cookieDomain = @group.AuthenticationDomain;
}
Expand Down Expand Up @@ -927,7 +952,7 @@ public static bool IsInRoles(string roles)
{
UserInfo objUserInfo = UserController.Instance.GetCurrentUserInfo();
PortalSettings settings = PortalController.Instance.GetCurrentPortalSettings();
return IsInRoles(objUserInfo, settings, roles);
return IsInRoles(objUserInfo, settings, roles);
}

public static bool IsInRoles(UserInfo objUserInfo, PortalSettings settings, string roles)
Expand All @@ -938,7 +963,7 @@ public static bool IsInRoles(UserInfo objUserInfo, PortalSettings settings, stri
if (!isInRoles)
{
if (roles != null)
{
{
foreach (string role in roles.Split(new[] { ';' }))
{
bool? roleAllowed;
Expand All @@ -951,7 +976,7 @@ public static bool IsInRoles(UserInfo objUserInfo, PortalSettings settings, stri
}
}
}
return isInRoles;
return isInRoles;
}

public static bool IsFriend(int userId)
Expand All @@ -974,6 +999,6 @@ public static bool IsOwner(int userId)
PortalSettings settings = PortalController.Instance.GetCurrentPortalSettings();
return IsInRoles(objUserInfo, settings, RoleOwnerPrefix + userId);
}
#endregion
}
#endregion
}
}
Loading