Authentication / Authorization

Who are you, anyway?
This commit is contained in:
Daniel J. Summers 2016-09-27 22:19:43 -05:00
parent 5235e5a5db
commit c0237e1433
7 changed files with 54 additions and 15 deletions

View File

@ -1,6 +1,8 @@
module MyPrayerJournal.App
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Localization
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.DependencyInjection
@ -31,13 +33,17 @@ type Startup(env : IHostingEnvironment) =
ignore <| services.AddLocalization (fun options -> options.ResourcesPath <- "Resources")
ignore <| services.AddMvc ()
ignore <| services.AddDistributedMemoryCache ()
ignore <| services.AddSession ()
// RethinkDB connection
async {
let cfg = services.BuildServiceProvider().GetService<IOptions<AppConfig>>().Value
let! conn = DataConfig.Connect cfg.DataConfig
do! conn.EstablishEnvironment cfg
ignore <| services.AddSingleton conn
//ignore <| services.AddDistributedRethinkDBCache (fun options ->
// options.Connection <- conn
// options.Database <- match cfg.DataConfig.Database with null -> "" | db -> db
// options.TableName <- "Session")
ignore <| services.AddSession ()
} |> Async.RunSynchronously
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -52,8 +58,14 @@ type Startup(env : IHostingEnvironment) =
ignore <| app.UseStaticFiles ()
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
ignore <| app.UseCookieAuthentication(
CookieAuthenticationOptions(
AuthenticationScheme = Keys.Authentication,
LoginPath = PathString("/user/log-on"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
ExpireTimeSpan = TimeSpan(2, 0, 0),
SlidingExpiration = true))
ignore <| app.UseMvc(fun routes ->
ignore <| routes.MapRoute(name = "default", template = "{controller=Home}/{action=Index}/{id?}"))

View File

@ -2,6 +2,9 @@
[<RequireQualifiedAccess>]
module MyPrayerJournal.Keys
/// Instance name for cookie authentication
let Authentication = "mpj-authentication"
/// The current user
let CurrentUser = "mpj-user"

View File

@ -1,12 +1,21 @@
namespace MyPrayerJournal.Controllers
open Microsoft.AspNetCore.Authorization
open Microsoft.AspNetCore.Mvc
open MyPrayerJournal
open RethinkDb.Driver.Net
/// Home controller
[<Authorize>]
[<Route("")>]
type HomeController(data : IConnection) =
inherit ApplicationController(data)
[<AllowAnonymous>]
[<HttpGet("")>]
member this.Index() = this.View()
member this.Index() =
async {
match this.HttpContext.User with
| :? AppUser as user -> return this.View "Dashboard" :> IActionResult
| _ -> return upcast this.View ()
} |> Async.StartAsTask

View File

@ -1,5 +1,6 @@
namespace MyPrayerJournal.Controllers
open Microsoft.AspNetCore.Authorization
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.Options
open MyPrayerJournal
@ -7,34 +8,42 @@ open MyPrayerJournal.ViewModels
open RethinkDb.Driver.Net
/// Controller for all /user URLs
[<Authorize>]
[<Route("user")>]
type UserController(data : IConnection, cfg : IOptions<AppConfig>) =
inherit ApplicationController(data)
[<AllowAnonymous>]
[<HttpGet("log-on")>]
member this.ShowLogOn () =
this.View(LogOnViewModel())
[<AllowAnonymous>]
[<HttpPost("log-on")>]
[<ValidateAntiForgeryToken>]
member this.DoLogOn (form : LogOnViewModel) =
async {
let! user = data.LogOnUser form.Email (User.HashPassword form.Password cfg.Value.PasswordSaltBytes)
match user with
| Some usr -> (* this.Session.[Keys.User] <- usr
| Some usr -> do! this.HttpContext.Authentication.SignInAsync(Keys.Authentication, AppUser(user))
// TODO: welcome message
(* this.Session.[Keys.User] <- usr
{ UserMessage.Empty with Level = Level.Info
Message = Strings.get "LogOnSuccess" }
|> model.AddMessage
this.Redirect "" model |> ignore // Save the messages in the session before the Nancy redirect
// TODO: investigate if addMessage should update the session when it's called
return this.LoginAndRedirect (System.Guid.Parse usr.Id, fallbackRedirectUrl = "/") :> obj
*)
|> model.AddMessage *)
return this.Redirect "/" :> IActionResult
| _ -> (*{ UserMessage.Empty with Level = Level.Error
Message = Strings.get "LogOnFailure" }
|> model.AddMessage
return this.Redirect "/user/log-on" model *)
return upcast this.RedirectToAction("ShowLogOn")
return upcast this.RedirectToAction "ShowLogOn"
//return this.View()
} |> Async.StartAsTask
[<HttpGet("log-off")>]
member this.LogOff () =
async {
do! this.HttpContext.Authentication.SignOutAsync(Keys.Authentication)
// TODO: goodbye message
return this.LocalRedirect "/"
} |> Async.StartAsTask

View File

@ -1,6 +1,7 @@
namespace MyPrayerJournal
open Newtonsoft.Json
open System.Security.Claims
open System.Security.Cryptography
/// A user
@ -82,3 +83,10 @@ type Request = {
|> List.map (fun item -> item.AsOf)
|> List.head
/// The user for use with identity
[<AllowNullLiteral>]
type AppUser(user : User option) =
inherit ClaimsPrincipal()
/// The current user
member val User = user with get

View File

@ -3,7 +3,6 @@ namespace MyPrayerJournal.ViewModels
//open MyPrayerJournal
/// Parent view model for all myPrayerJournal view models
[<AllowNullLiteral>]
type AppViewModel() =
member this.Q = "X"
(*

View File

@ -2,7 +2,6 @@ namespace MyPrayerJournal.ViewModels
open System.ComponentModel.DataAnnotations
[<AllowNullLiteral>]
type LogOnViewModel() =
inherit AppViewModel()