2020-12-04 02:21:21 +11:00
using System ;
2014-04-02 11:20:35 +02:00
using System.Collections.Specialized ;
2018-09-26 13:27:16 +02:00
using System.Net.Http ;
using System.Text ;
2019-01-11 14:30:04 +11:00
using System.Threading.Tasks ;
2020-08-20 22:18:50 +01:00
using Microsoft.Extensions.Options ;
2018-09-26 13:27:16 +02:00
using Newtonsoft.Json ;
2014-02-26 16:01:31 +01:00
using Umbraco.Core ;
2020-08-20 22:18:50 +01:00
using Umbraco.Core.Configuration.Models ;
2017-12-18 18:26:32 +01:00
using Umbraco.Core.Migrations.Install ;
2020-12-04 02:21:21 +11:00
using Umbraco.Core.Security ;
2016-09-01 19:06:08 +02:00
using Umbraco.Core.Services ;
2020-05-18 13:00:32 +01:00
using Umbraco.Extensions ;
2020-08-20 22:18:50 +01:00
using Umbraco.Web.Install.Models ;
2014-02-26 16:01:31 +01:00
namespace Umbraco.Web.Install.InstallSteps
{
2014-03-05 14:30:17 +11:00
/// <summary>
/// This is the first UI step for a brand new install
/// </summary>
/// <remarks>
2019-01-26 10:52:19 -05:00
/// By default this will show the user view which is the most basic information to configure a new install, but if an install get's interrupted because of an
2017-07-20 11:21:28 +02:00
/// error, etc... and the end-user refreshes the installer then we cannot show the user screen because they've already entered that information so instead we'll
2014-03-05 14:30:17 +11:00
/// display a simple continue installation view.
/// </remarks>
2018-03-21 16:01:49 +01:00
[InstallSetupStep(InstallationType.NewInstall, "User", 20, "")]
2014-03-05 14:30:17 +11:00
internal class NewInstallStep : InstallSetupStep < UserModel >
2014-02-26 16:01:31 +01:00
{
2016-09-01 19:06:08 +02:00
private readonly IUserService _userService ;
2016-11-29 10:31:25 +01:00
private readonly DatabaseBuilder _databaseBuilder ;
2018-09-26 13:27:16 +02:00
private static HttpClient _httpClient ;
2020-08-20 22:18:50 +01:00
private readonly UserPasswordConfigurationSettings _passwordConfiguration ;
private readonly SecuritySettings _securitySettings ;
2020-08-21 14:52:47 +01:00
private readonly ConnectionStrings _connectionStrings ;
2020-02-17 09:04:15 +01:00
private readonly ICookieManager _cookieManager ;
2020-09-22 14:44:41 +02:00
private readonly IBackOfficeUserManager _userManager ;
2014-02-26 16:01:31 +01:00
2020-08-20 22:18:50 +01:00
public NewInstallStep (
IUserService userService ,
DatabaseBuilder databaseBuilder ,
2020-08-23 23:36:48 +02:00
IOptions < UserPasswordConfigurationSettings > passwordConfiguration ,
IOptions < SecuritySettings > securitySettings ,
IOptions < ConnectionStrings > connectionStrings ,
2020-08-20 22:18:50 +01:00
ICookieManager cookieManager ,
2020-09-22 14:44:41 +02:00
IBackOfficeUserManager userManager )
2014-02-26 16:01:31 +01:00
{
2020-02-13 08:52:58 +01:00
_userService = userService ? ? throw new ArgumentNullException ( nameof ( userService ) ) ;
_databaseBuilder = databaseBuilder ? ? throw new ArgumentNullException ( nameof ( databaseBuilder ) ) ;
2020-08-20 22:18:50 +01:00
_passwordConfiguration = passwordConfiguration . Value ? ? throw new ArgumentNullException ( nameof ( passwordConfiguration ) ) ;
_securitySettings = securitySettings . Value ? ? throw new ArgumentNullException ( nameof ( securitySettings ) ) ;
2020-08-21 14:52:47 +01:00
_connectionStrings = connectionStrings . Value ? ? throw new ArgumentNullException ( nameof ( connectionStrings ) ) ;
2020-02-17 09:04:15 +01:00
_cookieManager = cookieManager ;
2020-05-17 08:48:36 +01:00
_userManager = userManager ? ? throw new ArgumentNullException ( nameof ( userManager ) ) ;
2014-02-26 16:01:31 +01:00
}
2019-11-25 21:20:00 +11:00
public override async Task < InstallSetupResult > ExecuteAsync ( UserModel user )
2014-02-26 16:01:31 +01:00
{
2018-05-31 23:05:35 +10:00
var admin = _userService . GetUserById ( Constants . Security . SuperUserId ) ;
2014-02-26 16:01:31 +01:00
if ( admin = = null )
{
2018-03-21 16:01:49 +01:00
throw new InvalidOperationException ( "Could not find the super user!" ) ;
2014-02-26 16:01:31 +01:00
}
2020-05-05 11:33:19 +02:00
admin . Email = user . Email . Trim ( ) ;
admin . Name = user . Name . Trim ( ) ;
admin . Username = user . Email . Trim ( ) ;
_userService . Save ( admin ) ;
2014-02-26 16:01:31 +01:00
2020-05-17 08:48:36 +01:00
var membershipUser = await _userManager . FindByIdAsync ( Constants . Security . SuperUserId . ToString ( ) ) ;
if ( membershipUser = = null )
{
throw new InvalidOperationException (
$"No user found in membership provider with id of {Constants.Security.SuperUserId}." ) ;
}
//To change the password here we actually need to reset it since we don't have an old one to use to change
var resetToken = await _userManager . GeneratePasswordResetTokenAsync ( membershipUser ) ;
if ( string . IsNullOrWhiteSpace ( resetToken ) )
throw new InvalidOperationException ( "Could not reset password: unable to generate internal reset token" ) ;
var resetResult = await _userManager . ChangePasswordWithResetAsync ( membershipUser . Id , resetToken , user . Password . Trim ( ) ) ;
if ( ! resetResult . Succeeded )
2020-05-18 13:00:32 +01:00
throw new InvalidOperationException ( "Could not reset password: " + string . Join ( ", " , resetResult . Errors . ToErrorMessage ( ) ) ) ;
2020-05-04 18:32:57 +01:00
2014-04-02 11:20:35 +02:00
if ( user . SubscribeToNewsLetter )
{
2018-09-26 13:27:16 +02:00
if ( _httpClient = = null )
_httpClient = new HttpClient ( ) ;
var values = new NameValueCollection { { "name" , admin . Name } , { "email" , admin . Email } } ;
var content = new StringContent ( JsonConvert . SerializeObject ( values ) , Encoding . UTF8 , "application/json" ) ;
2014-04-02 11:20:35 +02:00
try
{
2018-09-26 13:27:16 +02:00
var response = _httpClient . PostAsync ( "https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx" , content ) . Result ;
2014-04-02 11:20:35 +02:00
}
catch { /* fail in silence */ }
}
2019-11-25 21:20:00 +11:00
return null ;
2014-02-26 16:01:31 +01:00
}
2014-03-06 12:11:14 +11:00
/// <summary>
/// Return a custom view model for this step
/// </summary>
public override object ViewModel
{
get
{
return new
{
2019-11-25 21:20:00 +11:00
minCharLength = _passwordConfiguration . RequiredLength ,
2020-05-06 20:37:03 +02:00
minNonAlphaNumericLength = _passwordConfiguration . RequireNonLetterOrDigit ? 1 : 0 ,
quickInstallAvailable = DatabaseConfigureStep . IsSqlCeAvailable ( )
2014-03-06 12:11:14 +11:00
} ;
}
}
2014-02-27 10:16:30 +01:00
public override string View
{
2018-09-26 13:27:16 +02:00
get
{
return RequiresExecution ( null )
//the user UI
2017-07-20 11:21:28 +02:00
? "user"
2018-09-26 13:27:16 +02:00
//the continue install UI
: "continueinstall" ;
}
2014-03-04 11:16:42 +11:00
}
2017-07-20 11:21:28 +02:00
2014-03-05 14:30:17 +11:00
public override bool RequiresExecution ( UserModel model )
2014-03-04 11:16:42 +11:00
{
2014-03-04 19:20:36 +11:00
//now we have to check if this is really a new install, the db might be configured and might contain data
2020-09-15 09:11:36 +02:00
var databaseSettings = _connectionStrings . UmbracoConnectionString ;
2019-11-18 14:11:50 +01:00
if ( databaseSettings . IsConnectionStringConfigured ( ) & & _databaseBuilder . IsDatabaseConfigured )
2017-05-12 14:49:44 +02:00
return _databaseBuilder . HasSomeNonDefaultUser ( ) = = false ;
2017-07-20 11:21:28 +02:00
// In this one case when it's a brand new install and nothing has been configured, make sure the
2017-05-12 14:49:44 +02:00
// back office cookie is cleared so there's no old cookies lying around causing problems
2020-03-12 14:36:25 +01:00
_cookieManager . ExpireCookie ( _securitySettings . AuthCookieName ) ;
2016-05-25 14:36:00 +02:00
2014-03-04 19:20:36 +11:00
return true ;
2014-02-27 10:16:30 +01:00
}
2014-02-26 16:01:31 +01:00
}
2017-05-12 14:49:44 +02:00
}