Merge remote-tracking branch 'refs/remotes/umbraco/dev-v7' into dev-v7
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
namespace Umbraco.Core
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
@@ -15,6 +18,8 @@
|
||||
/// <summary>
|
||||
/// The auth cookie name
|
||||
/// </summary>
|
||||
[Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string AuthCookieName = "UMB_UCONTEXT";
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
@@ -15,7 +16,6 @@ using Microsoft.Owin;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
@@ -157,9 +157,6 @@ namespace Umbraco.Core.Security
|
||||
return new HttpContextWrapper(http).GetCurrentIdentity(authenticateRequestIfNotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This clears the forms authentication cookie
|
||||
/// </summary>
|
||||
public static void UmbracoLogout(this HttpContextBase http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
@@ -170,6 +167,8 @@ namespace Umbraco.Core.Security
|
||||
/// This clears the forms authentication cookie for webapi since cookies are handled differently
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
[Obsolete("Use OWIN IAuthenticationManager.SignOut instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static void UmbracoLogoutWebApi(this HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) throw new ArgumentNullException("response");
|
||||
@@ -195,11 +194,8 @@ namespace Umbraco.Core.Security
|
||||
response.Headers.AddCookies(new[] { authCookie, prevCookie, extLoginCookie });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This adds the forms authentication cookie for webapi since cookies are handled differently
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="user"></param>
|
||||
[Obsolete("Use WebSecurity.SetPrincipalForRequest")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static FormsAuthenticationTicket UmbracoLoginWebApi(this HttpResponseMessage response, IUser user)
|
||||
{
|
||||
if (response == null) throw new ArgumentNullException("response");
|
||||
@@ -250,26 +246,29 @@ namespace Umbraco.Core.Security
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
new HttpContextWrapper(http).UmbracoLogout();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Renews the Umbraco authentication ticket
|
||||
/// This will force ticket renewal in the OWIN pipeline
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
public static bool RenewUmbracoAuthTicket(this HttpContextBase http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
return RenewAuthTicket(http,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain,
|
||||
//Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way
|
||||
1440);
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will force ticket renewal in the OWIN pipeline
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool RenewUmbracoAuthTicket(this HttpContext http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
return new HttpContextWrapper(http).RenewUmbracoAuthTicket();
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -390,8 +389,7 @@ namespace Umbraco.Core.Security
|
||||
//ensure there's def an expired cookie
|
||||
http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName)
|
||||
@@ -432,51 +430,6 @@ namespace Umbraco.Core.Security
|
||||
return FormsAuthentication.Decrypt(formsCookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renews the forms authentication ticket & cookie
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="cookieName"></param>
|
||||
/// <param name="cookieDomain"></param>
|
||||
/// <param name="minutesPersisted"></param>
|
||||
/// <returns>true if there was a ticket to renew otherwise false if there was no ticket</returns>
|
||||
private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain, int minutesPersisted)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
//get the ticket
|
||||
var ticket = GetAuthTicket(http, cookieName);
|
||||
//renew the ticket
|
||||
var renewed = FormsAuthentication.RenewTicketIfOld(ticket);
|
||||
if (renewed == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//get the request cookie to get it's expiry date,
|
||||
//NOTE: this will never be null becaues we already do this
|
||||
// check in teh GetAuthTicket.
|
||||
var formsCookie = http.Request.Cookies[cookieName];
|
||||
|
||||
//encrypt it
|
||||
var hash = FormsAuthentication.Encrypt(renewed);
|
||||
//write it to the response
|
||||
var cookie = new HttpCookie(cookieName, hash)
|
||||
{
|
||||
Expires = DateTime.Now.AddMinutes(minutesPersisted),
|
||||
Domain = cookieDomain
|
||||
};
|
||||
|
||||
if (GlobalSettings.UseSSL)
|
||||
cookie.Secure = true;
|
||||
|
||||
//ensure http only, this should only be able to be accessed via the server
|
||||
cookie.HttpOnly = true;
|
||||
|
||||
//rewrite the cooke
|
||||
http.Response.Cookies.Set(cookie);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom FormsAuthentication ticket with the data specified
|
||||
/// </summary>
|
||||
|
||||
@@ -1,12 +1,38 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security.Cookies;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
public class BackOfficeCookieAuthenticationProvider : CookieAuthenticationProvider
|
||||
{
|
||||
public override void ResponseSignOut(CookieResponseSignOutContext context)
|
||||
{
|
||||
base.ResponseSignOut(context);
|
||||
|
||||
//Make sure the definitely all of these cookies are cleared when signing out with cookies
|
||||
context.Response.Cookies.Append(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "", new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddYears(-1),
|
||||
Path = "/"
|
||||
});
|
||||
context.Response.Cookies.Append(Constants.Web.PreviewCookieName, "", new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddYears(-1),
|
||||
Path = "/"
|
||||
});
|
||||
context.Response.Cookies.Append(Constants.Security.BackOfficeExternalCookieName, "", new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddYears(-1),
|
||||
Path = "/"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the culture is set correctly for the current back office user
|
||||
/// </summary>
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace Umbraco.Core.Security
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
string.Format(
|
||||
"User: {0} logged in from IP address {1}",
|
||||
userName,
|
||||
_request.RemoteIpAddress), null, null);
|
||||
break;
|
||||
case SignInStatus.LockedOut:
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
|
||||
@@ -100,6 +100,8 @@ namespace Umbraco.Core.Services
|
||||
|
||||
xml.Add(new XAttribute("loginName", member.Username));
|
||||
xml.Add(new XAttribute("email", member.Email));
|
||||
|
||||
xml.Add(new XAttribute("icon", member.ContentType.Icon));
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
173
src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs
Normal file
173
src/Umbraco.Tests/Mvc/RenderIndexActionSelectorAttributeTests.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Tests.Mvc
|
||||
{
|
||||
[TestFixture]
|
||||
public class RenderIndexActionSelectorAttributeTests
|
||||
{
|
||||
private MethodInfo GetRenderMvcControllerIndexMethodFromCurrentType(Type currType)
|
||||
{
|
||||
return currType.GetMethods().Single(x =>
|
||||
{
|
||||
if (x.Name != "Index") return false;
|
||||
if (x.ReturnParameter == null || x.ReturnParameter.ParameterType != typeof (ActionResult)) return false;
|
||||
var p = x.GetParameters();
|
||||
if (p.Length != 1) return false;
|
||||
if (p[0].ParameterType != typeof (RenderModel)) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Matches_Default_Index()
|
||||
{
|
||||
var attr = new RenderIndexActionSelectorAttribute();
|
||||
var req = new RequestContext();
|
||||
var appCtx = new ApplicationContext(
|
||||
CacheHelper.CreateDisabledCacheHelper(),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
Mock.Of<HttpContextBase>(),
|
||||
appCtx,
|
||||
new Mock<WebSecurity>(null, null).Object,
|
||||
Mock.Of<IUmbracoSettingsSection>(),
|
||||
Enumerable.Empty<IUrlProvider>(),
|
||||
true);
|
||||
var ctrl = new MatchesDefaultIndexController(umbCtx);
|
||||
var controllerCtx = new ControllerContext(req, ctrl);
|
||||
var result = attr.IsValidForRequest(controllerCtx,
|
||||
GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType()));
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Matches_Overriden_Index()
|
||||
{
|
||||
var attr = new RenderIndexActionSelectorAttribute();
|
||||
var req = new RequestContext();
|
||||
var appCtx = new ApplicationContext(
|
||||
CacheHelper.CreateDisabledCacheHelper(),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
Mock.Of<HttpContextBase>(),
|
||||
appCtx,
|
||||
new Mock<WebSecurity>(null, null).Object,
|
||||
Mock.Of<IUmbracoSettingsSection>(),
|
||||
Enumerable.Empty<IUrlProvider>(),
|
||||
true);
|
||||
var ctrl = new MatchesOverriddenIndexController(umbCtx);
|
||||
var controllerCtx = new ControllerContext(req, ctrl);
|
||||
var result = attr.IsValidForRequest(controllerCtx,
|
||||
GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType()));
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Matches_Custom_Index()
|
||||
{
|
||||
var attr = new RenderIndexActionSelectorAttribute();
|
||||
var req = new RequestContext();
|
||||
var appCtx = new ApplicationContext(
|
||||
CacheHelper.CreateDisabledCacheHelper(),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
Mock.Of<HttpContextBase>(),
|
||||
appCtx,
|
||||
new Mock<WebSecurity>(null, null).Object,
|
||||
Mock.Of<IUmbracoSettingsSection>(),
|
||||
Enumerable.Empty<IUrlProvider>(),
|
||||
true);
|
||||
var ctrl = new MatchesCustomIndexController(umbCtx);
|
||||
var controllerCtx = new ControllerContext(req, ctrl);
|
||||
var result = attr.IsValidForRequest(controllerCtx,
|
||||
GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType()));
|
||||
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Matches_Async_Index_Same_Signature()
|
||||
{
|
||||
var attr = new RenderIndexActionSelectorAttribute();
|
||||
var req = new RequestContext();
|
||||
var appCtx = new ApplicationContext(
|
||||
CacheHelper.CreateDisabledCacheHelper(),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
Mock.Of<HttpContextBase>(),
|
||||
appCtx,
|
||||
new Mock<WebSecurity>(null, null).Object,
|
||||
Mock.Of<IUmbracoSettingsSection>(),
|
||||
Enumerable.Empty<IUrlProvider>(),
|
||||
true);
|
||||
var ctrl = new MatchesAsyncIndexController(umbCtx);
|
||||
var controllerCtx = new ControllerContext(req, ctrl);
|
||||
var result = attr.IsValidForRequest(controllerCtx,
|
||||
GetRenderMvcControllerIndexMethodFromCurrentType(ctrl.GetType()));
|
||||
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
public class MatchesDefaultIndexController : RenderMvcController
|
||||
{
|
||||
public MatchesDefaultIndexController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchesOverriddenIndexController : RenderMvcController
|
||||
{
|
||||
public MatchesOverriddenIndexController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
public override ActionResult Index(RenderModel model)
|
||||
{
|
||||
return base.Index(model);
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchesCustomIndexController : RenderMvcController
|
||||
{
|
||||
public MatchesCustomIndexController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
public ActionResult Index(RenderModel model, int page)
|
||||
{
|
||||
return base.Index(model);
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchesAsyncIndexController : RenderMvcController
|
||||
{
|
||||
public MatchesAsyncIndexController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
public new async Task<ActionResult> Index(RenderModel model)
|
||||
{
|
||||
return await Task.FromResult(base.Index(model));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Web.Mvc;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Moq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
@@ -60,7 +61,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
//Cookie collection
|
||||
var cookieCollection = new HttpCookieCollection();
|
||||
cookieCollection.Add(new HttpCookie(Constants.Web.AuthCookieName, "FBA996E7-D6BE-489B-B199-2B0F3D2DD826"));
|
||||
cookieCollection.Add(new HttpCookie("UMB_UCONTEXT", "FBA996E7-D6BE-489B-B199-2B0F3D2DD826"));
|
||||
|
||||
//Request
|
||||
var requestMock = new Mock<HttpRequestBase>();
|
||||
|
||||
@@ -180,6 +180,7 @@
|
||||
<Compile Include="Logging\AsyncRollingFileAppenderTest.cs" />
|
||||
<Compile Include="Logging\DebugAppender.cs" />
|
||||
<Compile Include="Logging\ParallelForwarderTest.cs" />
|
||||
<Compile Include="Mvc\RenderIndexActionSelectorAttributeTests.cs" />
|
||||
<Compile Include="Persistence\Repositories\AuditRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\DomainRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\PartialViewRepositoryTests.cs" />
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"jquery-ui": "1.10.3",
|
||||
"angular-dynamic-locale": "~0.1.27",
|
||||
"tinymce": "~4.1.10",
|
||||
"codemirror": "~5.3.0"
|
||||
"codemirror": "~5.3.0",
|
||||
"ng-caps-lock": "~1.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +459,10 @@ module.exports = function (grunt) {
|
||||
},
|
||||
'angular-dynamic-locale': {
|
||||
files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map']
|
||||
},
|
||||
},
|
||||
'ng-caps-lock': {
|
||||
files: ['ng-caps-lock.min.js']
|
||||
},
|
||||
'codemirror': {
|
||||
files: [
|
||||
'lib/codemirror.js',
|
||||
|
||||
@@ -7,6 +7,7 @@ var app = angular.module('umbraco', [
|
||||
'ngCookies',
|
||||
'ngMobile',
|
||||
'ngSanitize',
|
||||
'ngCapsLock',
|
||||
/*'ui.sortable',*/
|
||||
'blueimp.fileupload',
|
||||
'tmh.dynamicLocale'
|
||||
|
||||
@@ -7,6 +7,7 @@ var app = angular.module('umbraco', [
|
||||
'ngCookies',
|
||||
'ngSanitize',
|
||||
'ngMobile',
|
||||
'ngCapsLock',
|
||||
'blueimp.fileupload',
|
||||
'tmh.dynamicLocale'
|
||||
]);
|
||||
|
||||
@@ -133,7 +133,7 @@ h5{
|
||||
.umb-control-group label.control-label {
|
||||
text-align: left
|
||||
}
|
||||
.umb-control-group label .help-block,
|
||||
.umb-control-group label .help-block,
|
||||
.umb-control-group label small {
|
||||
font-size: 11px;
|
||||
color: #a0a0a0;
|
||||
@@ -194,7 +194,7 @@ h5{
|
||||
|
||||
.umb-dashboard-control a{text-decoration: underline;}
|
||||
|
||||
.umb-dashboard-control
|
||||
.umb-dashboard-control
|
||||
iframe{ position: absolute; display: block; width: 99%; height: 99%; overflow: auto !important;}
|
||||
|
||||
|
||||
@@ -239,16 +239,16 @@ table thead a {
|
||||
|
||||
.umb-table tbody.ui-sortable tr.ui-sortable-helper {
|
||||
background-color: @sortableHelperBg;
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
.umb-table tbody.ui-sortable tr.ui-sortable-helper td
|
||||
.umb-table tbody.ui-sortable tr.ui-sortable-helper td
|
||||
{
|
||||
border:none;
|
||||
}
|
||||
|
||||
.umb-table tbody.ui-sortable tr.ui-sortable-placeholder {
|
||||
background-color: @sortablePlaceholderBg;
|
||||
border:none;
|
||||
border:none;
|
||||
}
|
||||
|
||||
.umb-table tbody.ui-sortable tr.ui-sortable-placeholder td
|
||||
@@ -368,7 +368,7 @@ table thead a {
|
||||
background: @inputBorder;
|
||||
height: 1px;
|
||||
margin: 20px 0 0 0;
|
||||
width: 82%;
|
||||
width: 82%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ table thead a {
|
||||
color:@green;
|
||||
}
|
||||
|
||||
// Loading Animation
|
||||
// Loading Animation
|
||||
// ------------------------
|
||||
|
||||
.umb-loader{
|
||||
@@ -523,11 +523,67 @@ height:1px;
|
||||
}
|
||||
|
||||
.umb-loader-wrapper {
|
||||
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin: 10px 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
// Input label styles
|
||||
// @Simon: not sure where to put this part yet
|
||||
// --- TODO Needs to be divided into the right .less directories
|
||||
|
||||
|
||||
// Titles for input fields
|
||||
.input-label--title {
|
||||
font-weight: bold;
|
||||
color: @black;
|
||||
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
|
||||
// Used for input checkmark fields
|
||||
.input-label--small {
|
||||
display: inline;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: fade(@black, 70);
|
||||
|
||||
&:hover {
|
||||
color: @black;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked + .input-label--small {
|
||||
color: @blue;
|
||||
}
|
||||
|
||||
|
||||
// Use this for headers in the panels
|
||||
.panel-dialog--header {
|
||||
border-bottom: 1px solid @gray;
|
||||
|
||||
margin: 10px 0;
|
||||
padding-bottom: 10px;
|
||||
|
||||
font-size: @fontSizeLarge;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,23 @@
|
||||
bottom: 90px;
|
||||
}
|
||||
|
||||
.umb-mediapicker-upload {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
.form-search {
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-panel.editor-breadcrumb .umb-panel-body, .umb-panel.editor-breadcrumb .umb-bottom-bar {
|
||||
bottom: 31px !important;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,16 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.umb-contentpicker small a {
|
||||
.umb-contentpicker small {
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-right: 3px;
|
||||
border-right: 1px solid @grayMed;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @gray;
|
||||
}
|
||||
}
|
||||
|
||||
/* CODEMIRROR DATATYPE */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
@grayDarker: #222;
|
||||
@grayDark: #343434;
|
||||
@gray: #555;
|
||||
@grayMed: #999;
|
||||
@grayLight: #d9d9d9;
|
||||
@grayLighter: #f8f8f8;
|
||||
@white: #fff;
|
||||
|
||||
@@ -10,6 +10,8 @@ LazyLoad.js(
|
||||
'lib/angular/1.1.5/angular-mobile.js',
|
||||
'lib/angular/1.1.5/angular-sanitize.min.js',
|
||||
|
||||
'lib/ng-caps-lock/ng-caps-lock.min.js',
|
||||
|
||||
'lib/angular/angular-ui-sortable.js',
|
||||
|
||||
'lib/angular-dynamic-locale/tmhDynamicLocale.min.js',
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
|
||||
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
|
||||
<p class="caps-lock-alert" ng-show="isCapsLockOn"><i class="icon-alert"></i> <localize key="login_capsLockIsOn">Caps Lock is on</localize></p>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
|
||||
|
||||
@@ -62,18 +62,16 @@
|
||||
<div class="umb-panel umb-dialogs-mediapicker browser" ng-hide="target" ng-style="{opacity: dropping == true ? '0.25' : '1.0'}">
|
||||
<div class="umb-panel-header">
|
||||
|
||||
<div class="umb-el-wrap umb-panel-buttons">
|
||||
<div class="span5">
|
||||
<div class="form-search">
|
||||
<i class="icon-search"></i>
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
class="umb-search-field search-query"
|
||||
placeholder="Filter...">
|
||||
</div>
|
||||
<div class="umb-el-wrap umb-panel-buttons umb-mediapicker-upload">
|
||||
<div class="form-search">
|
||||
<i class="icon-search"></i>
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
class="umb-search-field search-query"
|
||||
placeholder="Filter...">
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<div class="upload-button">
|
||||
<span class="btn fileinput-button" ng-class="{disabled: disabled}">
|
||||
<i class="icon-page-up"></i>
|
||||
<input type="file" name="files[]" multiple ng-disabled="disabled" class="uploader">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController',
|
||||
function($scope) {
|
||||
$scope.type = $scope.dialogOptions.type;
|
||||
$scope.section = {
|
||||
name: "",
|
||||
required: false
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
<form novalidate name="contentForm"
|
||||
ng-controller="Umbraco.Dialogs.Template.SnippetController"
|
||||
ng-submit="close()"
|
||||
val-form-manager>
|
||||
<div class="umb-panel">
|
||||
|
||||
<div class="umb-panel-header">
|
||||
<div class="panel-dialog--header" style="border-bottom: 1px solid whitesmoke">Configure the section</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-panel-body with-footer umb-querybuilder">
|
||||
<label>
|
||||
<div class="input-label--title">
|
||||
Section name:
|
||||
</div>
|
||||
<input type="text" ng-model="section.name"/>
|
||||
</label>
|
||||
<label ng-show="type === 'rendersection'">
|
||||
<input style="margin: 0;" type="checkbox" ng-checked="section.required" ng-model="section.required"/>
|
||||
<div class="input-label--small">
|
||||
<localize key="general_required">Required</localize>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="umb-panel-footer">
|
||||
<div class="umb-el-wrap umb-panel-buttons">
|
||||
<div class="btn-toolbar umb-btn-toolbar pull-right">
|
||||
<a href ng-click="close()" class="btn btn-link">
|
||||
<localize key="general_close">Close</localize>
|
||||
</a>
|
||||
<a href ng-click="submit(section)" class="btn btn-primary">Insert</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -8,17 +8,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="headline">{{user.name}}</h1>
|
||||
<small class="umb-version">Umbraco version {{version}}</small>
|
||||
<p class="muted">
|
||||
<small>
|
||||
<localize key="user_sessionExpires" />: {{remainingAuthSeconds | timespan}}
|
||||
</small>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-panel-body umb-scrollable">
|
||||
<div class="tab-content umb-control-group">
|
||||
|
||||
|
||||
<div class="umb-pane">
|
||||
<h5><localize key="user_yourProfile" /></h5>
|
||||
@@ -100,7 +99,5 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<small class="umb-version">Umbraco version {{version}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//this controller simply tells the dialogs service to open a mediaPicker window
|
||||
//with a specified callback, this callback will receive an object with a selection on it
|
||||
|
||||
function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper) {
|
||||
function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) {
|
||||
|
||||
function trim(str, chr) {
|
||||
var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
|
||||
@@ -49,6 +49,7 @@ function contentPickerController($scope, dialogService, entityResource, editorSt
|
||||
//the default pre-values
|
||||
var defaultConfig = {
|
||||
multiPicker: false,
|
||||
showOpenButton: false,
|
||||
showEditButton: false,
|
||||
showPathOnHover: false,
|
||||
startNode: {
|
||||
@@ -65,14 +66,17 @@ function contentPickerController($scope, dialogService, entityResource, editorSt
|
||||
|
||||
//Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that!
|
||||
$scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false);
|
||||
$scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false);
|
||||
$scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false);
|
||||
$scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false);
|
||||
|
||||
|
||||
var entityType = $scope.model.config.startNode.type === "member"
|
||||
? "Member"
|
||||
: $scope.model.config.startNode.type === "media"
|
||||
? "Media"
|
||||
: "Document";
|
||||
$scope.allowOpenButton = entityType === "Document" || entityType === "Media";
|
||||
$scope.allowEditButton = entityType === "Document";
|
||||
|
||||
//the dialog options for the picker
|
||||
var dialogOptions = {
|
||||
@@ -146,6 +150,21 @@ function contentPickerController($scope, dialogService, entityResource, editorSt
|
||||
$scope.renderModel.splice(index, 1);
|
||||
angularHelper.getCurrentForm($scope).$setDirty();
|
||||
};
|
||||
|
||||
$scope.showNode = function (index) {
|
||||
var item = $scope.renderModel[index];
|
||||
var id = item.id;
|
||||
var section = $scope.model.config.startNode.type.toLowerCase();
|
||||
|
||||
entityResource.getPath(id, entityType).then(function (path) {
|
||||
navigationService.changeSection(section);
|
||||
navigationService.showTree(section, {
|
||||
tree: section, path: path, forceReload: false, activate: true
|
||||
});
|
||||
var routePath = section + "/" + section + "/edit/" + id.toString();
|
||||
$location.path(routePath).search("");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.add = function (item) {
|
||||
var currIds = _.map($scope.renderModel, function (i) {
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
<i class="icon icon-delete red hover-show"></i>
|
||||
<i class="{{node.icon}} hover-hide"></i>
|
||||
{{node.name}}
|
||||
</a>
|
||||
<div ng-if="!dialogEditor && model.config.showEditButton">
|
||||
<small><a href umb-launch-mini-editor="node"><localize key="edit">Edit</localize></a></small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div ng-if="!dialogEditor && ((model.config.showOpenButton && allowOpenButton) || (model.config.showEditButton && allowEditButton))">
|
||||
<small ng-if="model.config.showOpenButton && allowOpenButton"><a href ng-click="showNode($index)"><localize key="open">Open</localize></a></small>
|
||||
<small ng-if="model.config.showEditButton && allowEditButton"><a href umb-launch-mini-editor="node"><localize key="edit">Edit</localize></a></small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -34,11 +36,10 @@
|
||||
<div class="help-inline" val-msg-for="minCount" val-toggle-msg="minCount">
|
||||
You need to add at least {{model.config.minNumber}} items
|
||||
</div>
|
||||
|
||||
|
||||
<div class="help-inline" val-msg-for="maxCount" val-toggle-msg="maxCount">
|
||||
You can only have {{model.config.maxNumber}} items selected
|
||||
</div>
|
||||
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
BIN
src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif
Normal file
BIN
src/Umbraco.Web.UI/Umbraco/Images/editor/renderbody.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +1,5 @@
|
||||
LazyLoad.js( [
|
||||
try {
|
||||
LazyLoad.js([
|
||||
'lib/jquery/jquery.min.js',
|
||||
/* 1.1.5 */
|
||||
'lib/angular/1.1.5/angular.min.js',
|
||||
@@ -13,5 +14,11 @@ LazyLoad.js( [
|
||||
jQuery(document).ready(function () {
|
||||
angular.bootstrap(document, ['ngSanitize', 'umbraco.install', 'umbraco.directives.validation']);
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (err.message == 'LazyLoad is not defined') {
|
||||
document.getElementById("feedback").style.display = "none";
|
||||
document.getElementById("missinglazyload").style.display = "block";
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -45,7 +45,20 @@
|
||||
<p ng-bind-html-unsafe="installer.fact"></p>
|
||||
</div>
|
||||
|
||||
<h3 ng-cloak ng-animate="'fade'" id="feedback" ng-show="installer.feedback">{{installer.feedback}}</h3>
|
||||
<h3 ng-cloak ng-animate="'fade'" id="feedback" ng-show="installer.feedback">{{installer.feedback}}</h3>
|
||||
|
||||
<div id="missinglazyload" style="display: none;">
|
||||
<h3>There has been a problem with the build.</h3>
|
||||
<p>This might be because you could be offline or on a slow connection. Please try the following steps</p>
|
||||
<ol>
|
||||
<li>Make sure you have <a href="https://nodejs.org" target="_blank">Node.js</a> installed.</li>
|
||||
<li>Open command prompt and cd to \src\Umbraco.Web.UI.Client.</li>
|
||||
<li>Check to see if \src\Umbraco.Web.UI.Client\node_modules folder exists (this could be hidden); if so delete it.</li>
|
||||
<li>Run npm install; if successfull the node_modules folder should be created in the Umbraco.Web.UI.Client directory.</li>
|
||||
<li>Run build\Build.bat.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var Umbraco = {};
|
||||
Umbraco.Sys = {};
|
||||
|
||||
@@ -490,7 +490,6 @@
|
||||
<key alias="renewSession">Forny for at gemme dine ændringer</key>
|
||||
</area>
|
||||
<area alias="login">
|
||||
|
||||
<key alias="greeting0">Så er det søndag!</key>
|
||||
<key alias="greeting1">Smil, det er mandag!</key>
|
||||
<key alias="greeting2">Hurra, det er tirsdag!</key>
|
||||
@@ -498,12 +497,9 @@
|
||||
<key alias="greeting4">Glædelig torsdag!</key>
|
||||
<key alias="greeting5">Endelig fredag!</key>
|
||||
<key alias="greeting6">Glædelig lørdag</key>
|
||||
|
||||
|
||||
<key alias="instruction">indtast brugernavn og kodeord</key>
|
||||
<key alias="timeout">Din session er udløbet</key>
|
||||
|
||||
|
||||
<key alias="capsLockIsOn">Caps Lock er slået til</key>
|
||||
<key alias="bottomText"><![CDATA[<p style="text-align:right;">© 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p> ]]></key>
|
||||
</area>
|
||||
<area alias="main">
|
||||
|
||||
@@ -531,7 +531,8 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
|
||||
<key alias="greeting6">Wunderbaren sonnigen Samstag</key>
|
||||
<key alias="instruction">Hier anmelden:</key>
|
||||
<key alias="timeout">Die Sitzung ist abgelaufen</key>
|
||||
<key alias="bottomText"><p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.org</a></p> </key>
|
||||
<key alias="capsLockIsOn">Caps Lock ist aktiviert</key>
|
||||
<key alias="bottomText"><p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.org</a></p> </key>
|
||||
</area>
|
||||
<area alias="main">
|
||||
<key alias="dashboard">Armaturenbrett</key>
|
||||
|
||||
@@ -561,6 +561,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="greeting6">Happy Caturday</key>
|
||||
<key alias="instruction">Log in below</key>
|
||||
<key alias="timeout">Session timed out</key>
|
||||
<key alias="capsLockIsOn">Caps Lock is on</key>
|
||||
<key alias="bottomText"><![CDATA[<p style="text-align:right;">© 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">Umbraco.com</a></p> ]]></key>
|
||||
</area>
|
||||
<area alias="main">
|
||||
|
||||
@@ -562,6 +562,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="greeting6">Happy Caturday</key>
|
||||
<key alias="instruction">Log in below</key>
|
||||
<key alias="timeout">Session timed out</key>
|
||||
<key alias="capsLockIsOn">Caps Lock is on</key>
|
||||
<key alias="bottomText"><![CDATA[<p style="text-align:right;">© 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">Umbraco.com</a></p> ]]></key>
|
||||
</area>
|
||||
<area alias="main">
|
||||
|
||||
@@ -248,29 +248,33 @@ namespace Umbraco.Web.UI.Umbraco.Settings.Views
|
||||
/// </summary>
|
||||
private void InitializeEditorForTemplate()
|
||||
{
|
||||
|
||||
//TODO: implement content placeholders, etc... just like we had in v5
|
||||
|
||||
//Panel1.Menu.InsertSplitter();
|
||||
//TODO: implement content placeholders, etc... just like we had in v5
|
||||
|
||||
//MenuIconI umbContainer = Panel1.Menu.NewIcon();
|
||||
//umbContainer.ImageURL = UmbracoPath + "/images/editor/masterpagePlaceHolder.gif";
|
||||
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
|
||||
//umbContainer.OnClickCommand =
|
||||
// ClientTools.Scripts.OpenModalWindow(
|
||||
// IOHelper.ResolveUrl(SystemDirectories.Umbraco) +
|
||||
// "/dialogs/insertMasterpagePlaceholder.aspx?&id=" + _template.Id,
|
||||
// ui.Text("template", "insertContentAreaPlaceHolder"), 470, 320);
|
||||
editorSource.Menu.InsertSplitter();
|
||||
|
||||
//MenuIconI umbContent = Panel1.Menu.NewIcon();
|
||||
//umbContent.ImageURL = UmbracoPath + "/images/editor/masterpageContent.gif";
|
||||
//umbContent.AltText = ui.Text("template", "insertContentArea");
|
||||
//umbContent.OnClickCommand =
|
||||
// ClientTools.Scripts.OpenModalWindow(
|
||||
// IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/dialogs/insertMasterpageContent.aspx?id=" +
|
||||
// _template.Id, ui.Text("template", "insertContentArea"), 470, 300);
|
||||
|
||||
}
|
||||
MenuIconI umbRenderBody = editorSource.Menu.NewIcon();
|
||||
umbRenderBody.ImageURL = UmbracoPath + "/images/editor/renderbody.gif";
|
||||
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
|
||||
umbRenderBody.AltText = "Insert @RenderBody()";
|
||||
|
||||
}
|
||||
umbRenderBody.OnClickCommand = "editViewEditor.insertRenderBody()";
|
||||
|
||||
MenuIconI umbSection = editorSource.Menu.NewIcon();
|
||||
umbSection.ImageURL = UmbracoPath + "/images/editor/masterpagePlaceHolder.gif";
|
||||
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
|
||||
umbSection.AltText = "Insert Section";
|
||||
|
||||
umbSection.OnClickCommand = "editViewEditor.openSnippetModal('section')";
|
||||
|
||||
MenuIconI umbRenderSection = editorSource.Menu.NewIcon();
|
||||
umbRenderSection.ImageURL = UmbracoPath + "/images/editor/masterpageContent.gif";
|
||||
//umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder");
|
||||
umbRenderSection.AltText = "Insert @RenderSection";
|
||||
|
||||
umbRenderSection.OnClickCommand = "editViewEditor.openSnippetModal('rendersection')";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@
|
||||
|
||||
UmbEditor.Insert("@Umbraco.RenderMacro(\"" + alias + "\")", "", this._opts.codeEditorElementId);
|
||||
},
|
||||
|
||||
insertRenderBody: function() {
|
||||
UmbEditor.Insert("@RenderBody()", "", this._opts.codeEditorElementId);
|
||||
},
|
||||
|
||||
openMacroModal: function (alias) {
|
||||
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
|
||||
@@ -54,6 +58,37 @@
|
||||
});
|
||||
},
|
||||
|
||||
openSnippetModal: function (type) {
|
||||
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
|
||||
|
||||
var self = this;
|
||||
|
||||
UmbClientMgr.openAngularModalWindow({
|
||||
template: "views/common/dialogs/template/snippet.html",
|
||||
callback: function (data) {
|
||||
|
||||
var code = "";
|
||||
|
||||
if (type === 'section') {
|
||||
code = "\n@section " + data.name + "{\n";
|
||||
code += "<!-- Content here -->\n" +
|
||||
"}\n";
|
||||
}
|
||||
|
||||
if (type === 'rendersection') {
|
||||
if (data.required) {
|
||||
code = "\n@RenderSection(\"" + data.name + "\", true);\n";
|
||||
} else {
|
||||
code = "\n@RenderSection(\"" + data.name + "\" false);\n";
|
||||
}
|
||||
}
|
||||
|
||||
UmbEditor.Insert(code, '', self._opts.codeEditorElementId);
|
||||
},
|
||||
type: type
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
openQueryModal: function () {
|
||||
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
|
||||
|
||||
@@ -177,19 +177,14 @@ namespace Umbraco.Web.Editors
|
||||
//get the user
|
||||
var user = Security.GetBackOfficeUser(loginModel.Username);
|
||||
var userDetail = Mapper.Map<UserDetail>(user);
|
||||
|
||||
//update the userDetail and set their remaining seconds
|
||||
userDetail.SecondsUntilTimeout = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
|
||||
|
||||
//create a response with the userDetail object
|
||||
var response = Request.CreateResponse(HttpStatusCode.OK, userDetail);
|
||||
|
||||
//set the response cookies with the ticket (NOTE: This needs to be done with the custom webapi extension because
|
||||
// we cannot mix HttpContext.Response.Cookies and the way WebApi/Owin work)
|
||||
var ticket = response.UmbracoLoginWebApi(user);
|
||||
|
||||
//This ensure the current principal is set, otherwise any logic executing after this wouldn't actually be authenticated
|
||||
http.Result.AuthenticateCurrentRequest(ticket, false);
|
||||
|
||||
//update the userDetail and set their remaining seconds
|
||||
userDetail.SecondsUntilTimeout = ticket.GetRemainingAuthSeconds();
|
||||
//ensure the user is set for the current request
|
||||
Request.SetPrincipalForRequest(user);
|
||||
|
||||
return response;
|
||||
|
||||
@@ -241,11 +236,12 @@ namespace Umbraco.Web.Editors
|
||||
/// Logs the current user out
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[UmbracoBackOfficeLogout]
|
||||
[ClearAngularAntiForgeryToken]
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public HttpResponseMessage PostLogout()
|
||||
{
|
||||
Request.TryGetOwinContext().Result.Authentication.SignOut();
|
||||
|
||||
Logger.Info<AuthenticationController>("User {0} from IP address {1} has logged out",
|
||||
() => User.Identity == null ? "UNKNOWN" : User.Identity.Name,
|
||||
() => TryGetOwinContext().Result.Request.RemoteIpAddress);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Web;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web
|
||||
@@ -62,6 +63,16 @@ namespace Umbraco.Web
|
||||
return request.Cookies[Constants.Web.PreviewCookieName] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a preview cookie exist ?
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPreviewCookie(this IOwinRequest request)
|
||||
{
|
||||
return request.Cookies[Constants.Web.PreviewCookieName] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a cookie exist with the specified key ?
|
||||
/// </summary>
|
||||
|
||||
@@ -43,14 +43,8 @@ namespace Umbraco.Web.Install.InstallSteps
|
||||
var security = new WebSecurity(_httpContext, _applicationContext);
|
||||
security.PerformLogin(0);
|
||||
|
||||
////Clear the auth cookie - this is required so that the login screen is displayed after upgrade and so the
|
||||
//// csrf anti-forgery tokens are created, otherwise there will just be JS errors if the user has an old
|
||||
//// login token from a previous version when we didn't have csrf tokens in place
|
||||
//var security = new WebSecurity(new HttpContextWrapper(Context), ApplicationContext.Current);
|
||||
//security.ClearCurrentLogin();
|
||||
|
||||
//reports the ended install
|
||||
InstallHelper ih = new InstallHelper(UmbracoContext.Current);
|
||||
var ih = new InstallHelper(UmbracoContext.Current);
|
||||
ih.InstallStatus(true, "");
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Mvc;
|
||||
@@ -36,6 +37,8 @@ namespace Umbraco.Web.Mvc
|
||||
/// <summary>
|
||||
/// Returns an instance of the default controller instance.
|
||||
/// </summary>
|
||||
[Obsolete("This method will be removed in future versions and should not be used to resolve a controller instance, the IControllerFactory is used for that purpose")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IRenderMvcController GetControllerInstance()
|
||||
{
|
||||
//try the dependency resolver, then the activator
|
||||
@@ -64,9 +67,9 @@ namespace Umbraco.Web.Mvc
|
||||
/// <param name="type"></param>
|
||||
private void ValidateType(Type type)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom<IRenderMvcController>(type) == false)
|
||||
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(type) == false)
|
||||
{
|
||||
throw new InvalidOperationException("The Type specified (" + type + ") is not of type " + typeof(IRenderMvcController));
|
||||
throw new InvalidOperationException("The Type specified (" + type + ") is not of type " + typeof (IRenderController));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
src/Umbraco.Web/Mvc/IRenderController.cs
Normal file
12
src/Umbraco.Web/Mvc/IRenderController.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker interface to designate that a controller will be used for Umbraco front-end requests and/or route hijacking
|
||||
/// </summary>
|
||||
public interface IRenderController : IController
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Web.Http.Filters;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Windows.Forms;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
@@ -8,7 +10,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <summary>
|
||||
/// The interface that must be implemented for a controller to be designated to execute for route hijacking
|
||||
/// </summary>
|
||||
public interface IRenderMvcController : IController
|
||||
public interface IRenderMvcController : IRenderController
|
||||
{
|
||||
/// <summary>
|
||||
/// The default action to render the front-end view
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
@@ -12,6 +13,8 @@ namespace Umbraco.Web.Mvc
|
||||
/// </summary>
|
||||
public class RenderActionInvoker : AsyncControllerActionInvoker
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
|
||||
/// </summary>
|
||||
@@ -26,14 +29,14 @@ namespace Umbraco.Web.Mvc
|
||||
//now we need to check if it exists, if not we need to return the Index by default
|
||||
if (ad == null)
|
||||
{
|
||||
//check if the controller is an instance of IRenderMvcController
|
||||
if (controllerContext.Controller is IRenderMvcController)
|
||||
//check if the controller is an instance of IRenderController and find the index
|
||||
if (controllerContext.Controller is IRenderController)
|
||||
{
|
||||
return controllerDescriptor.FindAction(controllerContext, "Index");
|
||||
}
|
||||
}
|
||||
return ad;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
42
src/Umbraco.Web/Mvc/RenderIndexActionSelectorAttribute.cs
Normal file
42
src/Umbraco.Web/Mvc/RenderIndexActionSelectorAttribute.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom ActionMethodSelector which will ensure that the RenderMvcController.Index(RenderModel model) action will be executed
|
||||
/// if the
|
||||
/// </summary>
|
||||
internal class RenderIndexActionSelectorAttribute : ActionMethodSelectorAttribute
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, ReflectedControllerDescriptor> ControllerDescCache = new ConcurrentDictionary<Type, ReflectedControllerDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the action method selection is valid for the specified controller context.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the action method selection is valid for the specified controller context; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="controllerContext">The controller context.</param><param name="methodInfo">Information about the action method.</param>
|
||||
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
|
||||
{
|
||||
var currType = methodInfo.ReflectedType;
|
||||
var baseType = methodInfo.DeclaringType;
|
||||
|
||||
//It's the same type, so this must be the Index action to use
|
||||
if (currType == baseType) return true;
|
||||
|
||||
if (currType == null) return false;
|
||||
|
||||
var controllerDesc = ControllerDescCache.GetOrAdd(currType, type => new ReflectedControllerDescriptor(currType));
|
||||
var actions = controllerDesc.GetCanonicalActions();
|
||||
|
||||
//If there are more than one Index action for this controller, then
|
||||
// this base class one should not be matched
|
||||
return actions.Count(x => x.ActionName == "Index") <= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A controller to render front-end requests
|
||||
/// The default controller to render front-end requests
|
||||
/// </summary>
|
||||
[PreRenderViewActionFilter]
|
||||
[PreRenderViewActionFilter]
|
||||
public class RenderMvcController : UmbracoController, IRenderMvcController
|
||||
{
|
||||
|
||||
@@ -64,7 +59,7 @@ namespace Umbraco.Web.Mvc
|
||||
{
|
||||
if (_publishedContentRequest != null)
|
||||
return _publishedContentRequest;
|
||||
if (!RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
|
||||
if (RouteData.DataTokens.ContainsKey("umbraco-doc-request") == false)
|
||||
{
|
||||
throw new InvalidOperationException("DataTokens must contain an 'umbraco-doc-request' key with a PublishedContentRequest object");
|
||||
}
|
||||
@@ -111,10 +106,10 @@ namespace Umbraco.Web.Mvc
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
[RenderIndexActionSelector]
|
||||
public virtual ActionResult Index(RenderModel model)
|
||||
{
|
||||
return CurrentTemplate(model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -310,8 +310,8 @@ namespace Umbraco.Web.Mvc
|
||||
//check if that controller exists
|
||||
if (controllerType != null)
|
||||
{
|
||||
//ensure the controller is of type 'IRenderMvcController' and ControllerBase
|
||||
if (TypeHelper.IsTypeAssignableFrom<IRenderMvcController>(controllerType)
|
||||
//ensure the controller is of type IRenderMvcController and ControllerBase
|
||||
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(controllerType)
|
||||
&& TypeHelper.IsTypeAssignableFrom<ControllerBase>(controllerType))
|
||||
{
|
||||
//set the controller and name to the custom one
|
||||
@@ -328,7 +328,7 @@ namespace Umbraco.Web.Mvc
|
||||
"The current Document Type {0} matches a locally declared controller of type {1}. Custom Controllers for Umbraco routing must implement '{2}' and inherit from '{3}'.",
|
||||
() => publishedContentRequest.PublishedContent.DocumentTypeAlias,
|
||||
() => controllerType.FullName,
|
||||
() => typeof(IRenderMvcController).FullName,
|
||||
() => typeof(IRenderController).FullName,
|
||||
() => typeof(ControllerBase).FullName);
|
||||
|
||||
//we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace Umbraco.Web.Mvc
|
||||
{
|
||||
if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode)
|
||||
{
|
||||
var text = value.ToString().ToLowerInvariant();
|
||||
var text = value.ToString();
|
||||
var pos = text.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
if (pos > -1)
|
||||
@@ -294,4 +294,4 @@ namespace Umbraco.Web.Mvc
|
||||
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public ContentPickerPropertyEditor()
|
||||
{
|
||||
_internalPreValues = new Dictionary<string, object>
|
||||
{
|
||||
{"showEditButton", "0"},
|
||||
{"startNodeId", "-1"},
|
||||
{"showPathOnHover", "0"}
|
||||
};
|
||||
{
|
||||
{"startNodeId", "-1"},
|
||||
{"showOpenButton", "0"},
|
||||
{"showEditButton", "0"},
|
||||
{"showPathOnHover", "0"}
|
||||
};
|
||||
}
|
||||
|
||||
private IDictionary<string, object> _internalPreValues;
|
||||
@@ -32,6 +33,9 @@ namespace Umbraco.Web.PropertyEditors
|
||||
|
||||
internal class ContentPickerPreValueEditor : PreValueEditor
|
||||
{
|
||||
[PreValueField("showOpenButton", "Show open button", "boolean")]
|
||||
public string ShowOpenButton { get; set; }
|
||||
|
||||
[PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")]
|
||||
public string ShowEditButton { get; set; }
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -15,10 +11,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public MultiNodeTreePickerPropertyEditor()
|
||||
{
|
||||
_internalPreValues = new Dictionary<string, object>
|
||||
{
|
||||
{"multiPicker", "1"},
|
||||
{"showEditButton", "0"}
|
||||
};
|
||||
{
|
||||
{"multiPicker", "1"},
|
||||
{"showOpenButton", "0"},
|
||||
{"showEditButton", "0"},
|
||||
{"showPathOnHover", "0"}
|
||||
};
|
||||
}
|
||||
|
||||
protected override PreValueEditor CreatePreValueEditor()
|
||||
@@ -47,9 +45,16 @@ namespace Umbraco.Web.PropertyEditors
|
||||
[PreValueField("maxNumber", "Maximum number of items", "number")]
|
||||
public string MaxNumber { get; set; }
|
||||
|
||||
|
||||
[PreValueField("showOpenButton", "Show open button", "boolean")]
|
||||
public string ShowOpenButton { get; set; }
|
||||
|
||||
[PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")]
|
||||
public string ShowEditButton { get; set; }
|
||||
|
||||
[PreValueField("showPathOnHover", "Show path when hovering items", "boolean")]
|
||||
public bool ShowPathOnHover { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This ensures the multiPicker pre-val is set based on the maxNumber of nodes set
|
||||
/// </summary>
|
||||
|
||||
@@ -155,7 +155,27 @@ namespace Umbraco.Web.Security.Identity
|
||||
UmbracoConfig.For.UmbracoSettings().Security,
|
||||
app.CreateLogger<GetUserSecondsMiddleWare>());
|
||||
|
||||
app.UseCookieAuthentication(authOptions);
|
||||
app.UseUmbracoBackOfficeCookieAuthentication(authOptions);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
internal static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException("app");
|
||||
}
|
||||
|
||||
//First the normal cookie middleware
|
||||
app.Use(typeof(CookieAuthenticationMiddleware), app, options);
|
||||
app.UseStageMarker(PipelineStage.Authenticate);
|
||||
|
||||
//Then our custom middlewares
|
||||
app.Use(typeof(ForceRenewalCookieAuthenticationMiddleware), app, options);
|
||||
app.UseStageMarker(PipelineStage.Authenticate);
|
||||
app.Use(typeof(FixWindowsAuthMiddlware));
|
||||
app.UseStageMarker(PipelineStage.Authenticate);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Microsoft.Owin;
|
||||
using System;
|
||||
using System.Web;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Infrastructure;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
@@ -33,8 +36,8 @@ namespace Umbraco.Web.Security.Identity
|
||||
return null;
|
||||
}
|
||||
|
||||
return UmbracoModule.ShouldAuthenticateRequest(
|
||||
context.HttpContextFromOwinContext().Request,
|
||||
return ShouldAuthenticateRequest(
|
||||
context,
|
||||
_umbracoContextAccessor.Value.OriginalRequestUrl) == false
|
||||
//Don't auth request, don't return a cookie
|
||||
? null
|
||||
@@ -42,5 +45,40 @@ namespace Umbraco.Web.Security.Identity
|
||||
: base.GetRequestCookie(context, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we should authenticate the request
|
||||
/// </summary>
|
||||
/// <param name="ctx"></param>
|
||||
/// <param name="originalRequestUrl"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We auth the request when:
|
||||
/// * it is a back office request
|
||||
/// * it is an installer request
|
||||
/// * it is a /base request
|
||||
/// * it is a preview request
|
||||
/// </remarks>
|
||||
internal static bool ShouldAuthenticateRequest(IOwinContext ctx, Uri originalRequestUrl)
|
||||
{
|
||||
var request = ctx.Request;
|
||||
var httpCtx = ctx.TryGetHttpContext();
|
||||
|
||||
if (//check the explicit flag
|
||||
ctx.Get<bool?>("umbraco-force-auth") != null
|
||||
|| (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null)
|
||||
//check back office
|
||||
|| request.Uri.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
|
||||
//check installer
|
||||
|| request.Uri.IsInstallerRequest()
|
||||
//detect in preview
|
||||
|| (request.HasPreviewCookie() && request.Uri != null && request.Uri.AbsolutePath.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)) == false)
|
||||
//check for base
|
||||
|| BaseRest.BaseRestHandler.IsBaseRestRequest(originalRequestUrl))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
51
src/Umbraco.Web/Security/Identity/FixWindowsAuthMiddlware.cs
Normal file
51
src/Umbraco.Web/Security/Identity/FixWindowsAuthMiddlware.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used to inspect the request to see if 2 x identities are assigned: A windows one and a back office one.
|
||||
/// When this is the case, it means that auth has executed for Windows & auth has executed for our back office cookie
|
||||
/// handler and now two identities have been assigned. Unfortunately, at some stage in the pipeline - I'm pretty sure
|
||||
/// it's the Role Provider Module - it again changes the user's Principal to a RolePrincipal and discards the second
|
||||
/// Identity which is the Back office identity thus preventing a user from accessing the back office... it's very annoying.
|
||||
///
|
||||
/// To fix this, we re-set the user Principal to only have a single identity: the back office one, since we know this is
|
||||
/// for a back office request.
|
||||
/// </summary>
|
||||
internal class FixWindowsAuthMiddlware : OwinMiddleware
|
||||
{
|
||||
public FixWindowsAuthMiddlware(OwinMiddleware next) : base(next)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task Invoke(IOwinContext context)
|
||||
{
|
||||
if (context.Request.Uri.IsClientSideRequest() == false)
|
||||
{
|
||||
var claimsPrincipal = context.Request.User as ClaimsPrincipal;
|
||||
if (claimsPrincipal != null
|
||||
&& claimsPrincipal.Identities.Count() > 1
|
||||
&& claimsPrincipal.Identities.Any(x => x is WindowsIdentity)
|
||||
&& claimsPrincipal.Identities.Any(x => x is UmbracoBackOfficeIdentity))
|
||||
{
|
||||
var backOfficeIdentity = claimsPrincipal.Identities.First(x => x is UmbracoBackOfficeIdentity);
|
||||
if (backOfficeIdentity.IsAuthenticated)
|
||||
{
|
||||
context.Request.User = new ClaimsPrincipal(backOfficeIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Next != null)
|
||||
{
|
||||
await Next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Cookies;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
|
||||
namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// If a flag is set on the context to force renew the ticket, this will do it
|
||||
/// </summary>
|
||||
internal class ForceRenewalCookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// This handler doesn't actually do any auth so we return null;
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
{
|
||||
return Task.FromResult((AuthenticationTicket)null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ticket from the request
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private AuthenticationTicket GetTicket()
|
||||
{
|
||||
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
|
||||
if (string.IsNullOrWhiteSpace(cookie))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var ticket = Options.TicketDataFormat.Unprotect(cookie);
|
||||
if (ticket == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will check if the token exists in the request to force renewal
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Task ApplyResponseGrantAsync()
|
||||
{
|
||||
//Now we need to check if we should force renew this based on a flag in the context
|
||||
|
||||
var httpCtx = Context.TryGetHttpContext();
|
||||
//check for the special flag in either the owin or http context
|
||||
var shouldRenew = Context.Get<bool?>("umbraco-force-auth") != null || (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null);
|
||||
|
||||
if (shouldRenew)
|
||||
{
|
||||
var signin = Helper.LookupSignIn(Options.AuthenticationType);
|
||||
var shouldSignin = signin != null;
|
||||
var signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode);
|
||||
var shouldSignout = signout != null;
|
||||
|
||||
//we don't care about the sign in/sign out scenario, only renewal
|
||||
if (shouldSignin == false && shouldSignout == false)
|
||||
{
|
||||
//get the ticket
|
||||
var model = GetTicket();
|
||||
if (model != null)
|
||||
{
|
||||
var currentUtc = Options.SystemClock.UtcNow;
|
||||
var issuedUtc = model.Properties.IssuedUtc;
|
||||
var expiresUtc = model.Properties.ExpiresUtc;
|
||||
|
||||
if (expiresUtc.HasValue && issuedUtc.HasValue)
|
||||
{
|
||||
//renew the date/times
|
||||
model.Properties.IssuedUtc = currentUtc;
|
||||
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
|
||||
model.Properties.ExpiresUtc = currentUtc.Add(timeSpan);
|
||||
|
||||
//now save back all the required cookie details
|
||||
var cookieValue = Options.TicketDataFormat.Protect(model);
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
Domain = Options.CookieDomain,
|
||||
HttpOnly = Options.CookieHttpOnly,
|
||||
Path = Options.CookiePath ?? "/",
|
||||
};
|
||||
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
|
||||
{
|
||||
cookieOptions.Secure = Request.IsSecure;
|
||||
}
|
||||
else
|
||||
{
|
||||
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
|
||||
}
|
||||
if (model.Properties.IsPersistent)
|
||||
{
|
||||
cookieOptions.Expires = model.Properties.ExpiresUtc.Value.ToUniversalTime().DateTime;
|
||||
}
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
cookieValue,
|
||||
cookieOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security.Cookies;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
using Owin;
|
||||
|
||||
namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// This middleware is used simply to force renew the auth ticket if a flag to do so is found in the request
|
||||
/// </summary>
|
||||
internal class ForceRenewalCookieAuthenticationMiddleware : CookieAuthenticationMiddleware
|
||||
{
|
||||
public ForceRenewalCookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, app, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return new ForceRenewalCookieAuthenticationHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,15 +76,15 @@ namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
var utcNow = DateTime.Now.ToUniversalTime();
|
||||
ticket.Properties.IssuedUtc = utcNow;
|
||||
ticket.Properties.ExpiresUtc = utcNow.AddMinutes(_authOptions.LoginTimeoutMinutes);
|
||||
ticket.Properties.ExpiresUtc = utcNow.AddMinutes(30);
|
||||
|
||||
var cookieValue = _authOptions.TicketDataFormat.Protect(ticket);
|
||||
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
Path = "/",
|
||||
Domain = _authOptions.CookieDomain ?? "FALSE",
|
||||
Expires = DateTime.Now.AddMinutes(_authOptions.LoginTimeoutMinutes),
|
||||
Domain = _authOptions.CookieDomain ?? null,
|
||||
Expires = DateTime.Now.AddMinutes(30),
|
||||
HttpOnly = true,
|
||||
Secure = _authOptions.CookieSecure == CookieSecureOption.Always
|
||||
|| (_authOptions.CookieSecure == CookieSecureOption.SameAsRequest && request.Uri.Scheme.InvariantEquals("https")),
|
||||
@@ -98,13 +98,13 @@ namespace Umbraco.Web.Security.Identity
|
||||
|
||||
remainingSeconds = (ticket.Properties.ExpiresUtc.Value - DateTime.Now.ToUniversalTime()).TotalSeconds;
|
||||
}
|
||||
else if (remainingSeconds <=30)
|
||||
else if (remainingSeconds <= 30)
|
||||
{
|
||||
//NOTE: We are using 30 seconds because that is what is coded into angular to force logout to give some headway in
|
||||
// the timeout process.
|
||||
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
string.Format("User logged will be logged out due to timeout: {0}, IP Address: {1}", ticket.Identity.Name, request.RemoteIpAddress),
|
||||
string.Format("User logged will be logged out due to timeout: {0}, IP Address: {1}", ticket.Identity.Name, request.RemoteIpAddress),
|
||||
null, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
}
|
||||
|
||||
SlidingExpiration = true;
|
||||
ExpireTimeSpan = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes);
|
||||
ExpireTimeSpan = TimeSpan.FromMinutes(LoginTimeoutMinutes);
|
||||
CookieDomain = securitySection.AuthCookieDomain;
|
||||
CookieName = securitySection.AuthCookieName;
|
||||
CookieHttpOnly = true;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Web;
|
||||
using System.Web;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Security.Identity
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
internal static class OwinExtensions
|
||||
{
|
||||
@@ -11,9 +12,10 @@ namespace Umbraco.Web.Security.Identity
|
||||
/// </summary>
|
||||
/// <param name="owinContext"></param>
|
||||
/// <returns></returns>
|
||||
internal static HttpContextBase HttpContextFromOwinContext(this IOwinContext owinContext)
|
||||
internal static Attempt<HttpContextBase> TryGetHttpContext(this IOwinContext owinContext)
|
||||
{
|
||||
return owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
|
||||
var ctx = owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
|
||||
return ctx == null ? Attempt<HttpContextBase>.Fail() : Attempt.Succeed(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
74
src/Umbraco.Web/Security/WebAuthExtensions.cs
Normal file
74
src/Umbraco.Web/Security/WebAuthExtensions.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.WebApi;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
internal static class WebAuthExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This will set a an authenticated IPrincipal to the current request given the IUser object
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
internal static IPrincipal SetPrincipalForRequest(this HttpRequestMessage request, IUser user)
|
||||
{
|
||||
var principal = new ClaimsPrincipal(
|
||||
new UmbracoBackOfficeIdentity(
|
||||
new ClaimsIdentity(),
|
||||
Mapper.Map<UserData>(user)));
|
||||
|
||||
//It is actually not good enough to set this on the current app Context and the thread, it also needs
|
||||
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
|
||||
// an underlying fault of asp.net not propogating the User correctly.
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.User = principal;
|
||||
}
|
||||
var http = request.TryGetHttpContext();
|
||||
if (http)
|
||||
{
|
||||
http.Result.User = principal;
|
||||
}
|
||||
Thread.CurrentPrincipal = principal;
|
||||
|
||||
//For WebAPI
|
||||
request.SetUserPrincipal(principal);
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will set a an authenticated IPrincipal to the current request given the IUser object
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
internal static IPrincipal SetPrincipalForRequest(this HttpContextBase httpContext, IUser user)
|
||||
{
|
||||
var principal = new ClaimsPrincipal(
|
||||
new UmbracoBackOfficeIdentity(
|
||||
new ClaimsIdentity(),
|
||||
Mapper.Map<UserData>(user)));
|
||||
|
||||
//It is actually not good enough to set this on the current app Context and the thread, it also needs
|
||||
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
|
||||
// an underlying fault of asp.net not propogating the User correctly.
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.User = principal;
|
||||
}
|
||||
httpContext.User = principal;
|
||||
Thread.CurrentPrincipal = principal;
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
@@ -8,8 +9,9 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using umbraco;
|
||||
using Microsoft.AspNet.Identity.Owin;
|
||||
using umbraco.businesslogic.Exceptions;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
|
||||
using User = umbraco.BusinessLogic.User;
|
||||
|
||||
@@ -84,32 +86,27 @@ namespace Umbraco.Web.Security
|
||||
/// <returns>returns the number of seconds until their session times out</returns>
|
||||
public virtual double PerformLogin(int userId)
|
||||
{
|
||||
var owinCtx = _httpContext.GetOwinContext();
|
||||
|
||||
var user = _applicationContext.Services.UserService.GetUserById(userId);
|
||||
return PerformLogin(user).GetRemainingAuthSeconds();
|
||||
var userDetail = Mapper.Map<UserDetail>(user);
|
||||
//update the userDetail and set their remaining seconds
|
||||
userDetail.SecondsUntilTimeout = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
|
||||
var principal = _httpContext.SetPrincipalForRequest(user);
|
||||
owinCtx.Authentication.SignIn((UmbracoBackOfficeIdentity)principal.Identity);
|
||||
return TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the user in
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns>returns the Forms Auth ticket created which is used to log them in</returns>
|
||||
[Obsolete("This method should not be used, login is performed by the OWIN pipeline, use the overload that returns double and accepts a UserId instead")]
|
||||
public virtual FormsAuthenticationTicket PerformLogin(IUser user)
|
||||
{
|
||||
//clear the external cookie - we do this without owin context because we're writing cookies directly to httpcontext
|
||||
// and cookie handling is different with httpcontext vs webapi and owin, normally we'd do:
|
||||
//_httpContext.GetOwinContext().Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
|
||||
var externalLoginCookie = _httpContext.Request.Cookies.Get(Constants.Security.BackOfficeExternalCookieName);
|
||||
if (externalLoginCookie != null)
|
||||
{
|
||||
externalLoginCookie.Expires = DateTime.Now.AddYears(-1);
|
||||
_httpContext.Response.Cookies.Set(externalLoginCookie);
|
||||
}
|
||||
|
||||
var ticket = _httpContext.CreateUmbracoAuthTicket(Mapper.Map<UserData>(user));
|
||||
|
||||
LogHelper.Info<WebSecurity>("User Id: {0} logged in", () => user.Id);
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
@@ -119,6 +116,7 @@ namespace Umbraco.Web.Security
|
||||
public virtual void ClearCurrentLogin()
|
||||
{
|
||||
_httpContext.UmbracoLogout();
|
||||
_httpContext.GetOwinContext().Authentication.SignOut();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
'lib/angular/1.1.5/angular-mobile.js',
|
||||
'lib/angular/1.1.5/angular-sanitize.min.js',
|
||||
|
||||
'lib/ng-caps-lock/ng-caps-lock.min.js',
|
||||
|
||||
'lib/angular/angular-ui-sortable.js',
|
||||
|
||||
'lib/angular-dynamic-locale/tmhDynamicLocale.min.js',
|
||||
|
||||
@@ -304,15 +304,22 @@
|
||||
<Compile Include="Media\EmbedProviders\Flickr.cs" />
|
||||
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
|
||||
<Compile Include="Models\PublishedContentWithKeyBase.cs" />
|
||||
<Compile Include="Mvc\IRenderController.cs" />
|
||||
<Compile Include="Mvc\RenderIndexActionSelectorAttribute.cs" />
|
||||
<Compile Include="PropertyEditors\DatePreValueEditor.cs" />
|
||||
<Compile Include="RequestLifespanMessagesFactory.cs" />
|
||||
<Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
|
||||
<Compile Include="Security\Identity\ExternalSignInAutoLinkOptions.cs" />
|
||||
<Compile Include="Security\Identity\FixWindowsAuthMiddlware.cs" />
|
||||
<Compile Include="Security\Identity\ForceRenewalCookieAuthenticationHandler.cs" />
|
||||
<Compile Include="Security\Identity\ForceRenewalCookieAuthenticationMiddleware.cs" />
|
||||
<Compile Include="Security\Identity\GetUserSecondsMiddleWare.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedJson.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedResponse.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedPhoto.cs" />
|
||||
<Compile Include="Security\Identity\IUmbracoBackOfficeTwoFactorOptions.cs" />
|
||||
<Compile Include="Security\OwinExtensions.cs" />
|
||||
<Compile Include="Security\WebAuthExtensions.cs" />
|
||||
<Compile Include="UmbracoDefaultOwinStartup.cs" />
|
||||
<Compile Include="IUmbracoContextAccessor.cs" />
|
||||
<Compile Include="Models\ContentEditing\Relation.cs" />
|
||||
@@ -569,7 +576,6 @@
|
||||
<Compile Include="Security\Identity\AuthenticationManagerExtensions.cs" />
|
||||
<Compile Include="Security\Identity\BackOfficeCookieManager.cs" />
|
||||
<Compile Include="Security\Identity\FormsAuthenticationSecureDataFormat.cs" />
|
||||
<Compile Include="Security\Identity\OwinExtensions.cs" />
|
||||
<Compile Include="Security\Identity\UmbracoBackOfficeCookieAuthOptions.cs" />
|
||||
<Compile Include="Scheduling\TaskAndFactoryExtensions.cs" />
|
||||
<Compile Include="SingletonUmbracoContextAccessor.cs" />
|
||||
|
||||
@@ -149,72 +149,7 @@ namespace Umbraco.Web
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we should authenticate the request
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="originalRequestUrl"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We auth the request when:
|
||||
/// * it is a back office request
|
||||
/// * it is an installer request
|
||||
/// * it is a /base request
|
||||
/// * it is a preview request
|
||||
/// </remarks>
|
||||
internal static bool ShouldAuthenticateRequest(HttpRequestBase request, Uri originalRequestUrl)
|
||||
{
|
||||
if (//check back office
|
||||
request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
|
||||
//check installer
|
||||
|| request.Url.IsInstallerRequest()
|
||||
//detect in preview
|
||||
|| (request.HasPreviewCookie() && request.Url != null && request.Url.AbsolutePath.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)) == false)
|
||||
//check for base
|
||||
|| BaseRest.BaseRestHandler.IsBaseRestRequest(originalRequestUrl))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//private static readonly ConcurrentHashSet<string> IgnoreTicketRenewUrls = new ConcurrentHashSet<string>();
|
||||
///// <summary>
|
||||
///// Determines if the authentication ticket should be renewed with a new timeout
|
||||
///// </summary>
|
||||
///// <param name="url"></param>
|
||||
///// <param name="httpContext"></param>
|
||||
///// <returns></returns>
|
||||
///// <remarks>
|
||||
///// We do not want to renew the ticket when we are checking for the user's remaining timeout unless -
|
||||
///// UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn == true
|
||||
///// </remarks>
|
||||
//internal static bool ShouldIgnoreTicketRenew(Uri url, HttpContextBase httpContext)
|
||||
//{
|
||||
// //this setting will renew the ticket for all requests.
|
||||
// if (UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// //initialize the ignore ticket urls - we don't need to lock this, it's concurrent and a hashset
|
||||
// // we don't want to have to gen the url each request so this will speed things up a teeny bit.
|
||||
// if (IgnoreTicketRenewUrls.Any() == false)
|
||||
// {
|
||||
// var urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
|
||||
// var checkSessionUrl = urlHelper.GetUmbracoApiService<AuthenticationController>(controller => controller.GetRemainingTimeoutSeconds());
|
||||
// IgnoreTicketRenewUrls.Add(checkSessionUrl);
|
||||
// }
|
||||
|
||||
// if (IgnoreTicketRenewUrls.Any(x => url.AbsolutePath.StartsWith(x)))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
|
||||
/// </summary>
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
using System.Web.Http.Filters;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A filter that is used to remove the authorization cookie for the current user when the request is successful
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used so that we can log a user OUT in conjunction with using other filters that modify the cookies collection.
|
||||
/// SD: I beleive this is a bug with web api since if you modify the cookies collection on the HttpContext.Current and then
|
||||
/// use a filter to write the cookie headers, the filter seems to have no affect at all.
|
||||
/// </remarks>
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future, use OWIN IAuthenticationManager.SignOut instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class UmbracoBackOfficeLogoutAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuted(HttpActionExecutedContext context)
|
||||
{
|
||||
if (context.Response == null) return;
|
||||
if (context.Response.IsSuccessStatusCode == false) return;
|
||||
|
||||
//this clears out all of our cookies
|
||||
context.Response.UmbracoLogoutWebApi();
|
||||
|
||||
//this calls the underlying owin sign out logic - which should call the
|
||||
// auth providers middleware callbacks if using custom auth middleware
|
||||
context.Request.TryGetOwinContext().Result.Authentication.SignOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1705,6 +1705,10 @@ namespace umbraco
|
||||
|
||||
// propagate the user's context
|
||||
// zb-00004 #29956 : refactor cookies names & handling
|
||||
|
||||
//TODO: This is the worst thing ever. This will also not work if people decide to put their own
|
||||
// custom auth system in place.
|
||||
|
||||
HttpCookie inCookie = StateHelper.Cookies.UserContext.RequestCookie;
|
||||
var cookie = new Cookie(inCookie.Name, inCookie.Value, inCookie.Path,
|
||||
HttpContext.Current.Request.ServerVariables["SERVER_NAME"]);
|
||||
|
||||
@@ -27,7 +27,9 @@ namespace dashboardUtilities
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out requestUri))
|
||||
{
|
||||
var feedProxyXml = xmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
|
||||
if (feedProxyXml != null && feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
|
||||
if (feedProxyXml != null
|
||||
&& feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null
|
||||
&& requestUri.Port == 80)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
|
||||
@@ -207,6 +207,9 @@ namespace UmbracoExamine
|
||||
if (e.Fields.ContainsKey("_searchEmail") == false)
|
||||
e.Fields.Add("_searchEmail", e.Node.Attribute("email").Value.Replace(".", " ").Replace("@", " "));
|
||||
}
|
||||
|
||||
if (e.Fields.ContainsKey(IconFieldName) == false)
|
||||
e.Fields.Add(IconFieldName, (string)e.Node.Attribute("icon"));
|
||||
}
|
||||
|
||||
private static XElement GetMemberItem(int nodeId)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace umbraco.BusinessLogic
|
||||
{
|
||||
@@ -350,7 +351,7 @@ namespace umbraco.BusinessLogic
|
||||
* that actually make sense? shouldn't some cookie have _no_ expires?
|
||||
*/
|
||||
static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, TimeSpan.Zero); // was "PreviewSet"
|
||||
static readonly Cookie _userContext = new Cookie(Constants.Web.AuthCookieName, 30d); // was "UserContext"
|
||||
static readonly Cookie _userContext = new Cookie(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, 30d); // was "UserContext"
|
||||
static readonly Cookie _member = new Cookie("UMB_MEMBER", 30d); // was "umbracoMember"
|
||||
|
||||
public static Cookie Preview { get { return _preview; } }
|
||||
|
||||
Reference in New Issue
Block a user