Skip to content

Commit b3aca08

Browse files
committed
Installation wizard is back.
1 parent cef72de commit b3aca08

38 files changed

+1997
-640
lines changed

src/Server/Coderr.Server.Infrastructure/IDatabaseUtilities.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,7 @@ public interface ISetupDatabaseTools
2929
/// </summary>
3030
/// <returns>Connection</returns>
3131
IDbConnection OpenConnection();
32+
33+
void TestConnection(string connectionString);
3234
}
3335
}

src/Server/Coderr.Server.SqlServer/Schema/Update.v10.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
create table IncidentHistory
99
(
1010
Id int not null identity primary key,
11-
IncidentId int not null constraint FK_IncidentEnvironment_Incidents REFERENCES Incidents(Id) ON DELETE CASCADE,
11+
IncidentId int not null constraint FK_IncidentHistory_Incidents REFERENCES Incidents(Id) ON DELETE CASCADE,
1212
CreatedAtUtc datetime not null,
1313
AccountId int NULL, -- for system entries
1414
State int not null,

src/Server/Coderr.Server.SqlServer/SqlServerTools.cs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Data.SqlClient;
34
using System.Diagnostics.CodeAnalysis;
45
using Coderr.Server.Abstractions;
56
using Coderr.Server.Infrastructure;
@@ -16,38 +17,41 @@ namespace Coderr.Server.SqlServer
1617
/// </remarks>
1718
public class SqlServerTools : ISetupDatabaseTools
1819
{
19-
private readonly IConnectionFactory _connectionFactory;
20+
private readonly Func<IDbConnection> _connectionFactory;
2021
private readonly SchemaManager _schemaManager;
2122

22-
public SqlServerTools(IConnectionFactory connectionFactory)
23+
public SqlServerTools(Func<IDbConnection> connectionFactory)
2324
{
2425
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
25-
_schemaManager = new SchemaManager(_connectionFactory.OpenConnection);
26+
_schemaManager = new SchemaManager(_connectionFactory);
2627
}
2728

2829
internal static string DbName { get; set; }
29-
30-
private bool IsConnectionConfigured => _connectionFactory.IsConfigured;
31-
30+
3231
/// <summary>
3332
/// Checks if the tables exists and are for the current DB schema.
3433
/// </summary>
3534
public bool GotUpToDateTables()
3635
{
37-
if (!IsConnectionConfigured)
38-
return false;
39-
40-
using (var con = _connectionFactory.OpenConnection())
36+
try
4137
{
42-
using (var cmd = con.CreateCommand())
38+
using (var con = _connectionFactory())
4339
{
44-
cmd.CommandText = "SELECT OBJECT_ID(N'dbo.[Accounts]', N'U')";
45-
var result = cmd.ExecuteScalar();
40+
using (var cmd = con.CreateCommand())
41+
{
42+
cmd.CommandText = "SELECT OBJECT_ID(N'dbo.[Accounts]', N'U')";
43+
var result = cmd.ExecuteScalar();
4644

47-
//null for SQL Express and DbNull for SQL Server
48-
return result != null && !(result is DBNull);
45+
//null for SQL Express and DbNull for SQL Server
46+
return result != null && !(result is DBNull);
47+
}
4948
}
5049
}
50+
catch (Exception)
51+
{
52+
return false;
53+
}
54+
5155
}
5256

5357
/// <summary>
@@ -75,9 +79,14 @@ public void CreateTables()
7579

7680
IDbConnection ISetupDatabaseTools.OpenConnection()
7781
{
78-
return _connectionFactory.OpenConnection();
82+
return _connectionFactory();
7983
}
8084

81-
85+
public void TestConnection(string connectionString)
86+
{
87+
var con = new SqlConnection(connectionString);
88+
con.Open();
89+
con.Dispose();
90+
}
8291
}
8392
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Security.Claims;
4+
using System.Threading.Tasks;
5+
using codeRR.Server.Web.Areas.Installation.Models;
6+
using Coderr.Server.Abstractions.Security;
7+
using Coderr.Server.Domain.Core.Account;
8+
using Coderr.Server.Domain.Core.Applications;
9+
using Coderr.Server.Domain.Core.User;
10+
using Coderr.Server.Infrastructure;
11+
using Coderr.Server.SqlServer.Core.Accounts;
12+
using Coderr.Server.SqlServer.Core.Applications;
13+
using Coderr.Server.SqlServer.Core.Users;
14+
using Griffin.Data;
15+
using Microsoft.AspNetCore.Authentication;
16+
using Microsoft.AspNetCore.Authentication.Cookies;
17+
using Microsoft.AspNetCore.Mvc;
18+
using Microsoft.AspNetCore.Mvc.Filters;
19+
20+
namespace codeRR.Server.Web.Areas.Installation.Controllers
21+
{
22+
[Area("Installation")]
23+
24+
public class AccountController : Controller
25+
{
26+
public ActionResult Admin()
27+
{
28+
SetStateFlag();
29+
var model = new AccountViewModel();
30+
return View(model);
31+
}
32+
33+
34+
[HttpPost]
35+
public async Task<ActionResult> Admin(AccountViewModel model)
36+
{
37+
SetStateFlag();
38+
39+
if (!ModelState.IsValid)
40+
return View(model);
41+
42+
try
43+
{
44+
var account = new Account(model.UserName, model.Password);
45+
account.Activate();
46+
account.IsSysAdmin = true;
47+
var con = SetupTools.DbTools.OpenConnection();
48+
var uow = new AdoNetUnitOfWork(con);
49+
var repos = new AccountRepository(uow);
50+
if (await repos.IsUserNameTakenAsync(model.UserName))
51+
return Redirect(Url.GetNextWizardStep());
52+
53+
account.SetVerifiedEmail(model.EmailAddress);
54+
await repos.CreateAsync(account);
55+
56+
var user = new User(account.Id, account.UserName)
57+
{
58+
EmailAddress = account.Email
59+
};
60+
var userRepos = new UserRepository(uow);
61+
await userRepos.CreateAsync(user);
62+
63+
var repos2 = new ApplicationRepository(uow);
64+
var app = new Application(user.AccountId, "DemoApp")
65+
{
66+
ApplicationType = TypeOfApplication.DesktopApplication
67+
};
68+
await repos2.CreateAsync(app);
69+
70+
var tm = new ApplicationTeamMember(app.Id, account.Id, "System")
71+
{
72+
Roles = new[] {ApplicationRole.Admin, ApplicationRole.Member},
73+
UserName = account.UserName
74+
};
75+
await repos2.CreateAsync(tm);
76+
77+
uow.SaveChanges();
78+
79+
var claims = new List<Claim>
80+
{
81+
new Claim(ClaimTypes.NameIdentifier, account.Id.ToString(), ClaimValueTypes.Integer32),
82+
new Claim(ClaimTypes.Name, account.UserName, ClaimValueTypes.String),
83+
new Claim(ClaimTypes.Email, account.Email, ClaimValueTypes.String),
84+
new Claim(CoderrClaims.Application, app.Id.ToString(), ClaimValueTypes.Integer32),
85+
new Claim(CoderrClaims.ApplicationAdmin, app.Id.ToString(), ClaimValueTypes.Integer32),
86+
new Claim(ClaimTypes.Role, CoderrRoles.SysAdmin, ClaimValueTypes.String)
87+
};
88+
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
89+
var properties = new AuthenticationProperties {IsPersistent = false};
90+
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
91+
new ClaimsPrincipal(identity), properties);
92+
93+
return Redirect(Url.GetNextWizardStep());
94+
}
95+
catch (Exception ex)
96+
{
97+
ViewBag.Exception = ex;
98+
ModelState.AddModelError("", ex.Message);
99+
return View(model);
100+
}
101+
}
102+
103+
private void SetStateFlag()
104+
{
105+
ViewBag.Exception = null;
106+
ViewBag.AlreadyCreated = false;
107+
if (User.Identity.IsAuthenticated)
108+
ViewBag.AlreadyCreated = true;
109+
else
110+
{
111+
using (var con = SetupTools.DbTools.OpenConnection())
112+
{
113+
using (var uow = new AdoNetUnitOfWork(con))
114+
{
115+
var id = uow.ExecuteScalar("SELECT TOP 1 Id FROM Accounts");
116+
if (id != null)
117+
ViewBag.AlreadyCreated = true;
118+
}
119+
}
120+
}
121+
122+
if (!ViewBag.AlreadyCreated)
123+
ViewBag.NextLink = null;
124+
}
125+
126+
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
127+
{
128+
ViewBag.PrevLink = Url.GetPreviousWizardStepLink();
129+
ViewBag.NextLink = Url.GetNextWizardStepLink();
130+
Response.Headers.Add("Cache-Control", "no-cache, no-store");
131+
Response.Headers.Add("Expires", "-1");
132+
return base.OnActionExecutionAsync(context, next);
133+
}
134+
}
135+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Mvc;
4+
5+
namespace codeRR.Server.Web.Areas.Installation.Controllers
6+
{
7+
/// <summary>
8+
/// Purpose is to be able to launch installation area and be able to use dependencies in the home controller
9+
/// </summary>
10+
11+
public class BootController : Controller
12+
{
13+
public ActionResult Index()
14+
{
15+
return RedirectToAction("Index", "Home");
16+
}
17+
18+
[AllowAnonymous/*, Route("installation/{*url}")*/]
19+
public ActionResult NoInstallation()
20+
{
21+
if (Request.Path.Value.EndsWith("/setup/activate", StringComparison.OrdinalIgnoreCase))
22+
return Redirect("~/?#/welcome/admin/");
23+
return View();
24+
}
25+
26+
[AllowAnonymous]
27+
public ActionResult ToInstall()
28+
{
29+
return RedirectToRoute(new { Controller = "Setup", Area = "Installation" });
30+
}
31+
32+
33+
}
34+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Threading.Tasks;
2+
using codeRR.Server.Web.Areas.Installation.Models;
3+
using Coderr.Server.Abstractions.Config;
4+
using Coderr.Server.App.Modules.Messaging.Commands;
5+
using Coderr.Server.Web;
6+
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.AspNetCore.Mvc.Filters;
8+
9+
namespace codeRR.Server.Web.Areas.Installation.Controllers
10+
{
11+
[Area("Installation")]
12+
13+
public class MessagingController : Controller
14+
{
15+
private ConfigurationStore _configStore;
16+
17+
public MessagingController(ConfigurationStore configStore)
18+
{
19+
_configStore = configStore;
20+
}
21+
22+
public ActionResult Email()
23+
{
24+
var model = new EmailViewModel();
25+
var settings = _configStore.Load<DotNetSmtpSettings>();
26+
if (!string.IsNullOrEmpty(settings.SmtpHost))
27+
{
28+
model.AccountName = settings.AccountName;
29+
model.PortNumber = settings.PortNumber;
30+
model.SmtpHost = settings.SmtpHost;
31+
model.UseSSL = settings.UseSsl;
32+
model.AccountPassword = settings.AccountPassword;
33+
}
34+
else
35+
{
36+
ViewBag.NextLink = "";
37+
}
38+
39+
return View(model);
40+
}
41+
42+
[HttpPost]
43+
public ActionResult Email(EmailViewModel model)
44+
{
45+
if (!ModelState.IsValid)
46+
return View(model);
47+
48+
var settings = new DotNetSmtpSettings
49+
{
50+
AccountName = model.AccountName,
51+
PortNumber = model.PortNumber ?? 25,
52+
AccountPassword = model.AccountPassword,
53+
SmtpHost = model.SmtpHost,
54+
UseSsl = model.UseSSL
55+
};
56+
_configStore.Store(settings);
57+
return Redirect(Url.GetNextWizardStep());
58+
}
59+
60+
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
61+
{
62+
Response.Headers.Add("Cache-Control", "no-cache, no-store");
63+
Response.Headers.Add("Expires", "-1");
64+
ViewBag.PrevLink = Url.GetPreviousWizardStepLink();
65+
ViewBag.NextLink = Url.GetNextWizardStepLink();
66+
return base.OnActionExecutionAsync(context, next);
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)