Add access levels (#19)

- Remove authorization level
This commit is contained in:
Daniel J. Summers 2022-07-16 15:51:58 -04:00
parent 07aff16c3a
commit 425223a3a8
7 changed files with 102 additions and 73 deletions

View File

@ -300,7 +300,7 @@ module Map =
passwordHash = getString "password_hash" rdr
salt = getGuid "salt" rdr
url = tryString "url" rdr
authorizationLevel = AuthorizationLevel.parse (getString "authorization_level" rdr)
accessLevel = AccessLevel.parse (getString "access_level" rdr)
}
/// Add a possibly-missing parameter, substituting null for None

View File

@ -20,7 +20,7 @@ type SQLiteWebLogUserData (conn : SqliteConnection) =
cmd.Parameters.AddWithValue ("@passwordHash", user.passwordHash)
cmd.Parameters.AddWithValue ("@salt", user.salt)
cmd.Parameters.AddWithValue ("@url", maybe user.url)
cmd.Parameters.AddWithValue ("@authorizationLevel", AuthorizationLevel.toString user.authorizationLevel)
cmd.Parameters.AddWithValue ("@accessLevel", AccessLevel.toString user.accessLevel)
] |> ignore
// IMPLEMENTATION FUNCTIONS
@ -31,10 +31,10 @@ type SQLiteWebLogUserData (conn : SqliteConnection) =
cmd.CommandText <- """
INSERT INTO web_log_user (
id, web_log_id, user_name, first_name, last_name, preferred_name, password_hash, salt, url,
authorization_level
access_level
) VALUES (
@id, @webLogId, @userName, @firstName, @lastName, @preferredName, @passwordHash, @salt, @url,
@authorizationLevel
@accessLevel
)"""
addWebLogUserParameters cmd user
do! write cmd
@ -103,7 +103,7 @@ type SQLiteWebLogUserData (conn : SqliteConnection) =
password_hash = @passwordHash,
salt = @salt,
url = @url,
authorization_level = @authorizationLevel
access_level = @accessLevel
WHERE id = @id
AND web_log_id = @webLogId"""
addWebLogUserParameters cmd user

View File

@ -174,7 +174,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger<SQLiteData>) =
password_hash TEXT NOT NULL,
salt TEXT NOT NULL,
url TEXT,
authorization_level TEXT NOT NULL);
access_level TEXT NOT NULL);
CREATE INDEX web_log_user_web_log_idx ON web_log_user (web_log_id);
CREATE INDEX web_log_user_user_name_idx ON web_log_user (web_log_id, user_name)"""
do! write cmd

View File

@ -436,8 +436,8 @@ type WebLogUser =
/// The URL of the user's personal site
url : string option
/// The user's authorization level
authorizationLevel : AuthorizationLevel
/// The user's access level
accessLevel : AccessLevel
}
/// Functions to support web log users
@ -454,7 +454,7 @@ module WebLogUser =
passwordHash = ""
salt = Guid.Empty
url = None
authorizationLevel = User
accessLevel = Author
}
/// Get the user's displayed name
@ -463,3 +463,7 @@ module WebLogUser =
seq { match user.preferredName with "" -> user.firstName | n -> n; " "; user.lastName }
|> Seq.reduce (+)
name.Trim ()
/// Does a user have the required access level?
let hasAccess level user =
AccessLevel.hasAccess level user.accessLevel

View File

@ -12,6 +12,51 @@ module private Helpers =
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-').Substring (0, 22)
/// A user's access level
type AccessLevel =
/// The user may create and publish posts and edit the ones they have created
| Author
/// The user may edit posts they did not create, but may not delete them
| Editor
/// The user may delete posts and configure web log settings
| WebLogAdmin
/// The user may manage themes (which affects all web logs for an installation)
| Administrator
/// Functions to support access levels
module AccessLevel =
/// Weightings for access levels
let private weights =
[ Author, 10
Editor, 20
WebLogAdmin, 30
Administrator, 40
]
|> Map.ofList
/// Convert an access level to its string representation
let toString =
function
| Author -> "Author"
| Editor -> "Editor"
| WebLogAdmin -> "WebLogAdmin"
| Administrator -> "Administrator"
/// Parse an access level from its string representation
let parse it =
match it with
| "Author" -> Author
| "Editor" -> Editor
| "WebLogAdmin" -> WebLogAdmin
| "Administrator" -> Administrator
| _ -> invalidOp $"{it} is not a valid access level"
/// Does a given access level allow an action that requires a certain access level?
let hasAccess needed held =
weights[needed] <= weights[held]
/// An identifier for a category
type CategoryId = CategoryId of string
@ -607,26 +652,6 @@ module WebLogId =
let create () = WebLogId (newId ())
/// A level of authorization for a given web log
type AuthorizationLevel =
/// <summary>The user may administer all aspects of a web log</summary>
| Administrator
/// <summary>The user is a known user of a web log</summary>
| User
/// Functions to support authorization levels
module AuthorizationLevel =
/// Convert an authorization level to a string
let toString = function Administrator -> "Administrator" | User -> "User"
/// Parse a string into an authorization level
let parse value =
match value with
| "Administrator" -> Administrator
| "User" -> User
| it -> invalidOp $"{it} is not a valid authorization level"
/// An identifier for a web log user
type WebLogUserId = WebLogUserId of string

View File

@ -47,7 +47,7 @@ let doLogOn : HttpHandler = fun next ctx -> task {
Claim (ClaimTypes.NameIdentifier, WebLogUserId.toString user.id)
Claim (ClaimTypes.Name, $"{user.firstName} {user.lastName}")
Claim (ClaimTypes.GivenName, user.preferredName)
Claim (ClaimTypes.Role, user.authorizationLevel.ToString ())
Claim (ClaimTypes.Role, AccessLevel.toString user.accessLevel)
}
let identity = ClaimsIdentity (claims, CookieAuthenticationDefaults.AuthenticationScheme)

View File

@ -48,7 +48,7 @@ let private doCreateWebLog (args : string[]) (sp : IServiceProvider) = task {
preferredName = "Admin"
passwordHash = Handlers.User.hashedPassword args[4] args[3] salt
salt = salt
authorizationLevel = Administrator
accessLevel = Administrator
}
// Create the default home page