Skip to content

Commit

Permalink
Merge pull request #3633 from donker/whitelist
Browse files Browse the repository at this point in the history
File extension whitelists for end users
  • Loading branch information
mitchelsellers authored Apr 11, 2020
2 parents 35c0213 + 75de3e2 commit 8fb142d
Show file tree
Hide file tree
Showing 28 changed files with 1,048 additions and 692 deletions.
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
{
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

0 comments on commit 8fb142d

Please sign in to comment.