2013-10-15 18:46:44 +11:00
using System ;
using System.Collections.Generic ;
2015-02-20 14:17:28 +01:00
using System.Linq ;
2013-08-12 15:06:12 +02:00
using System.Net ;
using System.Net.Http ;
2015-02-20 14:17:28 +01:00
using System.Security.Claims ;
2015-07-23 12:03:50 +02:00
using System.ServiceModel.Channels ;
2014-05-13 13:39:18 +10:00
using System.Text ;
2015-02-20 14:17:28 +01:00
using System.Threading.Tasks ;
2013-08-12 15:06:12 +02:00
using System.Web ;
2013-12-02 17:20:50 +11:00
using System.Web.Helpers ;
2013-08-12 15:06:12 +02:00
using System.Web.Http ;
2013-12-02 13:31:44 +11:00
using System.Web.Http.Controllers ;
2013-11-20 14:18:03 +11:00
using System.Web.Security ;
2013-08-26 11:28:15 +10:00
using AutoMapper ;
2015-02-06 13:47:00 +11:00
using Microsoft.AspNet.Identity ;
using Microsoft.Owin ;
using Microsoft.Owin.Security ;
2013-10-15 18:46:44 +11:00
using Umbraco.Core ;
2013-11-20 14:18:03 +11:00
using Umbraco.Core.Configuration ;
using Umbraco.Core.Models.Membership ;
2013-12-11 18:53:00 +11:00
using Umbraco.Web.Models ;
2013-08-12 15:06:12 +02:00
using Umbraco.Web.Models.ContentEditing ;
using Umbraco.Web.Models.Mapping ;
using Umbraco.Web.Mvc ;
2013-10-15 18:46:44 +11:00
using Umbraco.Core.Security ;
2013-08-12 15:06:12 +02:00
using Umbraco.Web.Security ;
using Umbraco.Web.WebApi ;
using Umbraco.Web.WebApi.Filters ;
2013-11-20 14:18:03 +11:00
using umbraco.providers ;
2015-02-20 14:17:28 +01:00
using Microsoft.AspNet.Identity.Owin ;
2015-05-10 17:47:32 +02:00
using Umbraco.Core.Logging ;
2015-02-20 14:17:28 +01:00
using Newtonsoft.Json.Linq ;
using Umbraco.Core.Models.Identity ;
2015-07-01 17:07:29 +02:00
using Umbraco.Web.Security.Identity ;
2015-03-25 10:57:10 +11:00
using IUser = Umbraco . Core . Models . Membership . IUser ;
2013-08-12 15:06:12 +02:00
namespace Umbraco.Web.Editors
{
/// <summary>
/// The API controller used for editing content
/// </summary>
[PluginController("UmbracoApi")]
[ValidationFilter]
2013-12-06 15:01:58 +11:00
[AngularJsonOnlyConfiguration]
2014-01-15 13:49:37 +11:00
[IsBackOffice]
2013-08-12 15:06:12 +02:00
public class AuthenticationController : UmbracoApiController
{
2015-03-24 20:17:37 +11:00
private BackOfficeUserManager _userManager ;
2015-07-01 17:07:29 +02:00
private BackOfficeSignInManager _signInManager ;
2015-03-24 20:17:37 +11:00
protected BackOfficeUserManager UserManager
{
2015-04-01 16:04:19 +11:00
get
{
if ( _userManager = = null )
{
var mgr = TryGetOwinContext ( ) . Result . GetUserManager < BackOfficeUserManager > ( ) ;
if ( mgr = = null )
{
throw new NullReferenceException ( "Could not resolve an instance of " + typeof ( BackOfficeUserManager ) + " from the " + typeof ( IOwinContext ) + " GetUserManager method" ) ;
}
_userManager = mgr ;
}
return _userManager ;
}
2015-03-24 20:17:37 +11:00
}
2015-07-01 17:07:29 +02:00
protected BackOfficeSignInManager SignInManager
{
get
{
if ( _signInManager = = null )
{
var mgr = TryGetOwinContext ( ) . Result . Get < BackOfficeSignInManager > ( ) ;
if ( mgr = = null )
{
throw new NullReferenceException ( "Could not resolve an instance of " + typeof ( BackOfficeSignInManager ) + " from the " + typeof ( IOwinContext ) ) ;
}
_signInManager = mgr ;
}
return _signInManager ;
}
}
2015-06-18 19:16:49 +02:00
2015-02-20 14:17:28 +01:00
[WebApi.UmbracoAuthorize]
[ValidateAngularAntiForgeryToken]
public async Task < HttpResponseMessage > PostUnLinkLogin ( UnLinkLoginModel unlinkLoginModel )
{
var result = await UserManager . RemoveLoginAsync (
User . Identity . GetUserId < int > ( ) ,
new UserLoginInfo ( unlinkLoginModel . LoginProvider , unlinkLoginModel . ProviderKey ) ) ;
if ( result . Succeeded )
{
var user = await UserManager . FindByIdAsync ( User . Identity . GetUserId < int > ( ) ) ;
2015-07-01 17:07:29 +02:00
await SignInManager . SignInAsync ( user , isPersistent : false , rememberBrowser : false ) ;
2015-02-20 14:17:28 +01:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
else
{
AddModelErrors ( result ) ;
return Request . CreateValidationErrorResponse ( ModelState ) ;
}
}
2013-08-12 15:06:12 +02:00
/// <summary>
2013-10-01 13:23:13 +10:00
/// Checks if the current user's cookie is valid and if so returns OK or a 400 (BadRequest)
/// </summary>
/// <returns></returns>
[HttpGet]
2014-05-13 13:39:18 +10:00
public bool IsAuthenticated ( )
2013-10-01 13:23:13 +10:00
{
var attempt = UmbracoContext . Security . AuthorizeRequest ( ) ;
if ( attempt = = ValidateRequestAttempt . Success )
{
2014-05-13 13:39:18 +10:00
return true ;
}
return false ;
2013-10-01 13:23:13 +10:00
}
/// <summary>
2013-10-15 18:46:44 +11:00
/// Returns the currently logged in Umbraco user
2013-08-12 15:06:12 +02:00
/// </summary>
/// <returns></returns>
2013-12-03 11:57:41 +11:00
/// <remarks>
/// We have the attribute [SetAngularAntiForgeryTokens] applied because this method is called initially to determine if the user
/// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf cookies are only session
/// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies.
/// </remarks>
2013-10-15 18:46:44 +11:00
[WebApi.UmbracoAuthorize]
2013-12-03 11:57:41 +11:00
[SetAngularAntiForgeryTokens]
2015-02-22 13:29:00 +01:00
public UserDetail GetCurrentUser ( )
2013-08-12 15:06:12 +02:00
{
2013-10-15 18:46:44 +11:00
var user = Services . UserService . GetUserById ( UmbracoContext . Security . GetUserId ( ) ) ;
var result = Mapper . Map < UserDetail > ( user ) ;
var httpContextAttempt = TryGetHttpContext ( ) ;
if ( httpContextAttempt . Success )
2013-08-12 15:06:12 +02:00
{
2013-10-15 18:46:44 +11:00
//set their remaining seconds
result . SecondsUntilTimeout = httpContextAttempt . Result . GetRemainingAuthSeconds ( ) ;
2013-08-12 15:06:12 +02:00
}
2015-02-20 14:17:28 +01:00
2013-10-15 18:46:44 +11:00
return result ;
2013-08-12 15:06:12 +02:00
}
2015-02-22 13:29:00 +01:00
[WebApi.UmbracoAuthorize]
2015-03-24 20:17:37 +11:00
[ValidateAngularAntiForgeryToken]
2015-02-22 13:29:00 +01:00
public async Task < Dictionary < string , string > > GetCurrentUserLinkedLogins ( )
{
var identityUser = await UserManager . FindByIdAsync ( UmbracoContext . Security . GetUserId ( ) ) ;
return identityUser . Logins . ToDictionary ( x = > x . LoginProvider , x = > x . ProviderKey ) ;
}
2013-08-12 15:06:12 +02:00
/// <summary>
/// Logs a user in
/// </summary>
/// <returns></returns>
2013-12-03 11:36:17 +11:00
[SetAngularAntiForgeryTokens]
2015-03-25 10:57:10 +11:00
public async Task < HttpResponseMessage > PostLogin ( LoginModel loginModel )
2013-08-12 15:06:12 +02:00
{
2015-05-10 17:47:32 +02:00
var http = this . TryGetHttpContext ( ) ;
if ( http . Success = = false )
throw new InvalidOperationException ( "This method requires that an HttpContext be active" ) ;
2015-07-01 17:07:29 +02:00
var result = await SignInManager . PasswordSignInAsync (
loginModel . Username , loginModel . Password , isPersistent : true , shouldLockout : true ) ;
switch ( result )
2013-08-12 15:06:12 +02:00
{
2015-07-01 17:07:29 +02:00
case SignInStatus . Success :
//get the user
var user = Security . GetBackOfficeUser ( loginModel . Username ) ;
var userDetail = Mapper . Map < UserDetail > ( user ) ;
2015-11-19 18:12:21 +01:00
//update the userDetail and set their remaining seconds
userDetail . SecondsUntilTimeout = TimeSpan . FromMinutes ( GlobalSettings . TimeOutInMinutes ) . TotalSeconds ;
2015-07-01 17:07:29 +02:00
//create a response with the userDetail object
var response = Request . CreateResponse ( HttpStatusCode . OK , userDetail ) ;
2015-11-19 18:12:21 +01:00
//ensure the user is set for the current request
Request . SetPrincipalForRequest ( user ) ;
2015-07-01 17:07:29 +02:00
return response ;
2013-08-12 15:06:12 +02:00
2015-07-01 17:07:29 +02:00
case SignInStatus . RequiresVerification :
var twofactorOptions = UserManager as IUmbracoBackOfficeTwoFactorOptions ;
if ( twofactorOptions = = null )
{
throw new HttpResponseException (
Request . CreateErrorResponse (
HttpStatusCode . BadRequest ,
"UserManager does not implement " + typeof ( IUmbracoBackOfficeTwoFactorOptions ) ) ) ;
}
var twofactorView = twofactorOptions . GetTwoFactorView (
TryGetOwinContext ( ) . Result ,
UmbracoContext ,
loginModel . Username ) ;
if ( twofactorView . IsNullOrWhiteSpace ( ) )
{
throw new HttpResponseException (
Request . CreateErrorResponse (
HttpStatusCode . BadRequest ,
typeof ( IUmbracoBackOfficeTwoFactorOptions ) + ".GetTwoFactorView returned an empty string" ) ) ;
}
//create a with information to display a custom two factor send code view
var verifyResponse = Request . CreateResponse ( HttpStatusCode . OK , new
{
twoFactorView = twofactorView
} ) ;
return verifyResponse ;
case SignInStatus . LockedOut :
case SignInStatus . Failure :
default :
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
// by our angular helper because it thinks that we need to re-perform the request once we are
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
throw new HttpResponseException ( HttpStatusCode . BadRequest ) ;
}
2013-08-12 15:06:12 +02:00
}
2013-11-20 14:18:03 +11:00
2013-08-12 15:06:12 +02:00
/// <summary>
/// Logs the current user out
/// </summary>
/// <returns></returns>
2013-12-02 17:20:50 +11:00
[ClearAngularAntiForgeryToken]
2013-12-03 11:36:17 +11:00
[ValidateAngularAntiForgeryToken]
2013-08-12 15:06:12 +02:00
public HttpResponseMessage PostLogout ( )
2015-07-23 12:03:50 +02:00
{
2015-11-19 18:12:21 +01:00
Request . TryGetOwinContext ( ) . Result . Authentication . SignOut ( ) ;
2015-07-23 12:03:50 +02:00
Logger . Info < AuthenticationController > ( "User {0} from IP address {1} has logged out" ,
( ) = > User . Identity = = null ? "UNKNOWN" : User . Identity . Name ,
( ) = > TryGetOwinContext ( ) . Result . Request . RemoteIpAddress ) ;
2013-08-12 15:06:12 +02:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
2015-03-24 20:17:37 +11:00
private void AddModelErrors ( IdentityResult result , string prefix = "" )
{
foreach ( var error in result . Errors )
{
ModelState . AddModelError ( prefix , error ) ;
}
}
2013-08-12 15:06:12 +02:00
}
2013-06-17 01:06:31 +02:00
}