A middle layer to provide consistent user authorisation independent of the methods of authentication and authorisation.
The package exports:
default_user()
- the default username, if only a single user is required (mainly used to keep the app functional without any specialisation)default_role()
- the default role that is attributed to each user, default:user
get_user()
- obtain the current user, needs to be specialised (can be any type), default:default_user()
get_roles()
- obtain the roles of the current user, needs to be specialised, default:user !== nothing ? [default_role(App)] : String[]
is_authorised(; role = default_role())
- return whether the current user has the role or any of the roles defined by therole
keyword, can be specialised
Customization is typically done by specialising get_user()
and get_roles()
, it is also possible to specialise is_authorised()
but not recommended.
As the middle layer is designed to allow for app-dependent authorisation, specialisation needs provide the app type it is meant to work with, e.g.
using GenieAuthentication
StippleUsers.get_user(::AllApps) = get_authentication()
to provide the user id of GenieAuthentication for all apps. Note that const AllApps = Typ{<:ReactiveModel}
has been defined for convenience.
StippleUsers.get_user(::Type{SnowApp}) = get(headers(), "sf-context-current-user", "not_authorised")
StippleUsers.get_roles(::Type{SnowApp}, user::String) = user == "not_authorised" ? String[] : [default_role()]
to provide the Snowpark user for the (explicit) app SnowApp
using Stipple, Stipple.ReactiveTools, StippleUsers
@app MyApp begin
@in x = 1
end
# define roles as the first part of the string
StippleUsers.get_roles(::AllApps, user::String) = String[split(user, '_')[1]]
# case 1: normal user
StippleUsers.get_user(::AllApps) = "user_1"
get_roles()
# ["user"]
is_authorised()
# true
is_authorised(role = "admin")
# false
# case 2: Admin
StippleUsers.get_user(::AllApps) = "admin_1"
get_roles()
# ["admin"]
is_authorised()
# false
is_authorised(role = "admin")
# true
using Stipple, Stipple.ReactiveTools, StippleUsers
@app MyApp begin
@in x = 1
end
# make roles identical to user
StippleUsers.get_roles(::Type{MyApp}, user::String) = [user]
# case 1: normal user
StippleUsers.get_user(::Type{MyApp}) = "user_1"
get_roles(MyApp)
# ["user_1"]
is_authorised(MyApp)
# false
userlist() = ["user_1", "admin_1"]
is_authorised(MyApp, role = userlist())
# true
# case 2: Admin
StippleUsers.get_user(::Type{MyApp}) = "admin_1"
get_roles(MyApp)
# ["admin_1"]
is_authorised(MyApp, role = userlist())
# true
using Stipple, Stipple.ReactiveTools, StippleUsers
# define an implicit model
@app begin
@in x = 1
end
userlist() = ["user_1", "admin_1"]
# define `App` as the module's implicit model
App = Stipple.@type
# define `is_authorised()` for the implicit App
# Note that it is important to allow for kwargs, because internal handling will call `is_authorised()` with the `role` kwarg.
StippleUsers.is_authorised(::Type{App}, user::String; kwargs...) = user ∈ userlist()
StippleUsers.get_user(::Type{App}) = "user_1"
is_authorised(App)
# true
StippleUsers.get_user(::Type{App}) = "user_2"
is_authorised(App)
# false
StippleUsers.get_user(::Type{App}) = "admin_1"
is_authorised(App)
# true
using Stipple, Stipple.ReactiveTools, StippleUsers
@app MyApp begin
@in x = 1
end
# create a random user or admin
StippleUsers.get_user(::Type{MyApp}) = "$(rand(("user", "admin")))_$(rand(1:100))"
# define the role as first part of the string
StippleUsers.get_roles(::Type{MyApp}, user::String) = String[split(user, '_')[1]]
# define an admin check which returns nothing in case of success and a not-found-page in case of failure
admin_check() = is_authorised(MyApp, role = "admin") ? nothing : not_found()
ui() = "Hello Admin!"
@page("/", ui, model = MyApp, pre = admin_check)
up(open_browser = true)
# reload the page several times; depending on whether you are a (random) admin, the page will be shown or not