GetExtra, SetExtra, and matching LS updates.
parent
3c203a2e48
commit
4b7caff906
|
@ -11,6 +11,8 @@ AuthWS = (socket-url) ->
|
||||||
"get-user-by-credentials": 3
|
"get-user-by-credentials": 3
|
||||||
"mod-user": 4
|
"mod-user": 4
|
||||||
"register": 5
|
"register": 5
|
||||||
|
"get-extra": 6
|
||||||
|
"set-extra": 7
|
||||||
}
|
}
|
||||||
|
|
||||||
response-types = {
|
response-types = {
|
||||||
|
@ -19,6 +21,8 @@ AuthWS = (socket-url) ->
|
||||||
"user": 2
|
"user": 2
|
||||||
"user-added": 3
|
"user-added": 3
|
||||||
"user-edited": 4
|
"user-edited": 4
|
||||||
|
"extra": 5
|
||||||
|
"extra-updated": 6
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: naming convention
|
# TODO: naming convention
|
||||||
|
@ -90,6 +94,19 @@ AuthWS = (socket-url) ->
|
||||||
password: password
|
password: password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.get-extra = (token, name) ->
|
||||||
|
self.send request-types[\get-extra], JSON.stringify {
|
||||||
|
token: token
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set-extra = (token, name, extra) ->
|
||||||
|
self.send request-types[\set-extra], JSON.stringify {
|
||||||
|
token: token
|
||||||
|
name: name
|
||||||
|
extra: extra
|
||||||
|
}
|
||||||
|
|
||||||
# TODO: authd overhaul required
|
# TODO: authd overhaul required
|
||||||
#self.add-user = (login, password) ->
|
#self.add-user = (login, password) ->
|
||||||
# self.send request-types[\add-user], JSON.stringify {
|
# self.send request-types[\add-user], JSON.stringify {
|
||||||
|
|
|
@ -15,18 +15,26 @@ model = {
|
||||||
token: void
|
token: void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authws-url = "ws://localhost:9999/auth.JSON"
|
||||||
|
|
||||||
document.add-event-listener \DOMContentLoaded ->
|
document.add-event-listener \DOMContentLoaded ->
|
||||||
user-config-panel = void
|
user-config-panel = void
|
||||||
|
|
||||||
login-form = LoginForm {
|
login-form = LoginForm {
|
||||||
enable-registration: true
|
enable-registration: true
|
||||||
authws-url: "ws://localhost:9999/auth.JSON"
|
authws-url: authws-url
|
||||||
|
|
||||||
on-login: (user, token) ->
|
on-login: (user, token) ->
|
||||||
model.user := user
|
model.user := user
|
||||||
model.token := token
|
model.token := token
|
||||||
|
|
||||||
user-config-panel := UserConfigurationPanel model.user, model.token
|
user-config-panel := UserConfigurationPanel {
|
||||||
|
authhw-url: authws-url
|
||||||
|
user: model.user
|
||||||
|
token: model.token
|
||||||
|
|
||||||
|
on-model-update: -> projector.schedule-render!
|
||||||
|
}
|
||||||
|
|
||||||
projector.schedule-render!
|
projector.schedule-render!
|
||||||
on-error: (error) ->
|
on-error: (error) ->
|
||||||
|
@ -52,9 +60,7 @@ document.add-event-listener \DOMContentLoaded ->
|
||||||
else if user-config-panel
|
else if user-config-panel
|
||||||
h \div.section [
|
h \div.section [
|
||||||
h \div.container [
|
h \div.container [
|
||||||
h \div.box [
|
user-config-panel.render!
|
||||||
user-config-panel.render!
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,39 +1,156 @@
|
||||||
|
|
||||||
{h} = require "maquette"
|
{h} = require "maquette"
|
||||||
|
|
||||||
UserConfigurationPanel = (user, token) ->
|
AuthWS = require "./authws.ls"
|
||||||
self = {}
|
|
||||||
|
|
||||||
console.log user
|
get-full-name = (self) ->
|
||||||
|
full-name = self.profile && self.profile.full-name || ""
|
||||||
|
if full-name == ""
|
||||||
|
self.user.login
|
||||||
|
else
|
||||||
|
full-name
|
||||||
|
|
||||||
|
default-side-bar-renderer = (self) ->
|
||||||
|
h \div { key: \side-bar } [
|
||||||
|
h \figure.image.is-128x128.is-clipped [
|
||||||
|
if self.profile && self.profile.avatar
|
||||||
|
h \img {
|
||||||
|
src: self.profile.avatar
|
||||||
|
alt: "Avatar of #{get-full-name self}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
default-heading-renderer = (self) ->
|
||||||
|
full-name = get-full-name self
|
||||||
|
|
||||||
|
h \div.section {key: \heading} [
|
||||||
|
h \div.title.is-2 [ full-name ]
|
||||||
|
|
||||||
|
if full-name != self.user.login
|
||||||
|
h \div.title.is-3.subtitle [
|
||||||
|
self.user.login
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
Fields = {
|
||||||
|
render-text-input: (token, auth-ws, key, inputs, model) ->
|
||||||
|
upload = ->
|
||||||
|
console.log "clickity click", key, inputs[key], inputs
|
||||||
|
return unless inputs[key]
|
||||||
|
|
||||||
|
payload = {}
|
||||||
|
for _key, value of model
|
||||||
|
payload[_key] = value
|
||||||
|
payload[key] = inputs[key]
|
||||||
|
|
||||||
|
inputs[key] := void
|
||||||
|
|
||||||
|
auth-ws.set-extra token, "profile", payload
|
||||||
|
|
||||||
|
h \div.field.has-addons {key: key} [
|
||||||
|
h \div.control.is-expanded [
|
||||||
|
h \input.input {
|
||||||
|
value: inputs[key] || model[key]
|
||||||
|
oninput: (e) ->
|
||||||
|
console.log "input for",key
|
||||||
|
inputs[key] := e.target.value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
h \div.control [
|
||||||
|
h \div.button {
|
||||||
|
onclick: upload
|
||||||
|
} [ "Update" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
UserConfigurationPanel = (args) ->
|
||||||
|
self = {
|
||||||
|
user: args.user || {}
|
||||||
|
profile: args.profile
|
||||||
|
token: args.token
|
||||||
|
authws-url: args.authws-url ||
|
||||||
|
((if location.protocol == 'https' then 'wss' else 'ws') +
|
||||||
|
'://' + location.hostname + ":9999/auth.JSON")
|
||||||
|
|
||||||
|
side-bar-renderer: args.side-bar-renderer || default-side-bar-renderer
|
||||||
|
heading-renderer: args.heading-renderer || default-heading-renderer
|
||||||
|
|
||||||
|
on-model-update: args.on-model-update || ->
|
||||||
|
|
||||||
|
input: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
auth-ws = AuthWS self.authws-url
|
||||||
|
|
||||||
|
auth-ws.add-event-listener \extra, (message) ->
|
||||||
|
if message.name == "profile"
|
||||||
|
console.log "got profile", message.extra
|
||||||
|
self.profile = message.extra || {}
|
||||||
|
|
||||||
|
self.on-model-update!
|
||||||
|
|
||||||
|
auth-ws.add-event-listener \extra-updated, (message) ->
|
||||||
|
if message.name == "profile"
|
||||||
|
console.log "got profile", message.extra
|
||||||
|
self.profile = message.extra || {}
|
||||||
|
|
||||||
|
self.on-model-update!
|
||||||
|
|
||||||
|
unless self.profile
|
||||||
|
auth-ws.socket.onopen = ->
|
||||||
|
auth-ws.get-extra self.token, "profile"
|
||||||
|
|
||||||
self.render = ->
|
self.render = ->
|
||||||
full-name = user.full_name
|
|
||||||
if full-name == ""
|
|
||||||
full-name = user.login
|
|
||||||
|
|
||||||
h \div.columns {
|
h \div.columns {
|
||||||
key: self
|
key: self
|
||||||
} [
|
} [
|
||||||
h \div.column.is-one-quarter [
|
h \div.column.is-narrow [
|
||||||
h \figure.image.is-128 [
|
self.side-bar-renderer self
|
||||||
h \img {
|
|
||||||
# FIXME
|
|
||||||
url: "https://bulma.io/images/placeholders/128x128.png"
|
|
||||||
alt: "Avatar of #{full-name}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
h \div.column [
|
h \div.column [
|
||||||
h \div.title.is-2 [ full-name ]
|
self.heading-renderer self
|
||||||
|
|
||||||
if full-name != user.login
|
if self.profile
|
||||||
h \div.title.is-3.subtitle [
|
h \div.box {key: \profile} [
|
||||||
user.login
|
h \div.form [
|
||||||
|
h \div.title.is-4 [ "Profile" ]
|
||||||
|
h \div.label {key: \full-name-label} [ "Full Name" ]
|
||||||
|
Fields.render-text-input self.token, auth-ws, "fullName", self.input, self.profile
|
||||||
|
|
||||||
|
h \div.label {key: \profile-picture-label} [ "Profile Picture" ]
|
||||||
|
Fields.render-text-input self.token, auth-ws, "avatar", self.input, self.profile
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
else
|
||||||
|
# FIXME: urk, ugly loader.
|
||||||
|
h \div.button.is-loading
|
||||||
|
|
||||||
h \div.title.is-4 [ "Permissions" ]
|
h \div.box {key: \passwd} [
|
||||||
h \div.tags user.groups.map (group) ->
|
h \div.title.is-4 [ "Permissions" ]
|
||||||
h \div.tag [ group ]
|
|
||||||
|
h \div.form [
|
||||||
|
h \div.field {key: \uid} [
|
||||||
|
h \div.label [ "User ID" ]
|
||||||
|
h \div.control [ self.user.uid.to-string! ]
|
||||||
|
]
|
||||||
|
|
||||||
|
h \div.field {key: \gid} [
|
||||||
|
h \div.label [ "Group ID" ]
|
||||||
|
h \div.control [ self.user.gid.to-string! ]
|
||||||
|
]
|
||||||
|
|
||||||
|
h \div.field {key: \groups} [
|
||||||
|
h \div.label [ "Groups" ]
|
||||||
|
h \div.control.is-grouped [
|
||||||
|
h \div.tags self.user.groups.map (group) ->
|
||||||
|
h \div.tag [ group ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -24,5 +24,8 @@ dependencies:
|
||||||
jwt:
|
jwt:
|
||||||
github: crystal-community/jwt
|
github: crystal-community/jwt
|
||||||
branch: master
|
branch: master
|
||||||
|
fs:
|
||||||
|
git: https://git.karchnu.fr/WeirdOS/fs.cr
|
||||||
|
branch: master
|
||||||
|
|
||||||
license: EUPL
|
license: EUPL
|
||||||
|
|
27
src/authd.cr
27
src/authd.cr
|
@ -66,6 +66,22 @@ class AuthD::Response
|
||||||
initialize :uid
|
initialize :uid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Extra < Response
|
||||||
|
property user : Int32
|
||||||
|
property name : String
|
||||||
|
property extra : JSON::Any?
|
||||||
|
|
||||||
|
initialize :user, :name, :extra
|
||||||
|
end
|
||||||
|
|
||||||
|
class ExtraUpdated < Response
|
||||||
|
property user : Int32
|
||||||
|
property name : String
|
||||||
|
property extra : JSON::Any?
|
||||||
|
|
||||||
|
initialize :user, :name, :extra
|
||||||
|
end
|
||||||
|
|
||||||
# This creates a Request::Type enumeration. One entry for each request type.
|
# This creates a Request::Type enumeration. One entry for each request type.
|
||||||
{% begin %}
|
{% begin %}
|
||||||
enum Type
|
enum Type
|
||||||
|
@ -191,6 +207,17 @@ class AuthD::Request
|
||||||
initialize :login, :password
|
initialize :login, :password
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Request::GetExtra < Request
|
||||||
|
property token : String
|
||||||
|
property name : String
|
||||||
|
end
|
||||||
|
|
||||||
|
class Request::SetExtra < Request
|
||||||
|
property token : String
|
||||||
|
property name : String
|
||||||
|
property extra : JSON::Any
|
||||||
|
end
|
||||||
|
|
||||||
# This creates a Request::Type enumeration. One entry for each request type.
|
# This creates a Request::Type enumeration. One entry for each request type.
|
||||||
{% begin %}
|
{% begin %}
|
||||||
enum Type
|
enum Type
|
||||||
|
|
34
src/main.cr
34
src/main.cr
|
@ -5,6 +5,7 @@ require "openssl"
|
||||||
require "jwt"
|
require "jwt"
|
||||||
require "passwd"
|
require "passwd"
|
||||||
require "ipc"
|
require "ipc"
|
||||||
|
require "fs"
|
||||||
|
|
||||||
require "./authd.cr"
|
require "./authd.cr"
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ extend AuthD
|
||||||
class AuthD::Service
|
class AuthD::Service
|
||||||
property registrations_allowed = false
|
property registrations_allowed = false
|
||||||
|
|
||||||
def initialize(@passwd : Passwd, @jwt_key : String)
|
def initialize(@passwd : Passwd, @jwt_key : String, @extras_root : String)
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_request(request : AuthD::Request?, connection : IPC::Connection)
|
def handle_request(request : AuthD::Request?, connection : IPC::Connection)
|
||||||
|
@ -80,11 +81,35 @@ class AuthD::Service
|
||||||
user = @passwd.add_user request.login, request.password
|
user = @passwd.add_user request.login, request.password
|
||||||
|
|
||||||
Response::UserAdded.new user
|
Response::UserAdded.new user
|
||||||
|
when Request::GetExtra
|
||||||
|
user = get_user_from_token request.token
|
||||||
|
|
||||||
|
return Response::Error.new "invalid token" unless user
|
||||||
|
|
||||||
|
storage = FS::Hash(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
|
||||||
|
|
||||||
|
Response::Extra.new user.uid, request.name, storage[request.name]?
|
||||||
|
when Request::SetExtra
|
||||||
|
user = get_user_from_token request.token
|
||||||
|
|
||||||
|
return Response::Error.new "invalid token" unless user
|
||||||
|
|
||||||
|
storage = FS::Hash(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
|
||||||
|
|
||||||
|
storage[request.name] = request.extra
|
||||||
|
|
||||||
|
Response::ExtraUpdated.new user.uid, request.name, request.extra
|
||||||
else
|
else
|
||||||
Response::Error.new "unhandled request type"
|
Response::Error.new "unhandled request type"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_user_from_token(token)
|
||||||
|
user, meta = JWT.decode token, @jwt_key, JWT::Algorithm::HS256
|
||||||
|
|
||||||
|
Passwd::User.from_json user.to_json
|
||||||
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
##
|
##
|
||||||
# Provides a JWT-based authentication scheme for service-specific users.
|
# Provides a JWT-based authentication scheme for service-specific users.
|
||||||
|
@ -115,6 +140,7 @@ authd_passwd_file = "passwd"
|
||||||
authd_group_file = "group"
|
authd_group_file = "group"
|
||||||
authd_jwt_key = "nico-nico-nii"
|
authd_jwt_key = "nico-nico-nii"
|
||||||
authd_registrations = false
|
authd_registrations = false
|
||||||
|
authd_extra_storage = "storage"
|
||||||
|
|
||||||
OptionParser.parse do |parser|
|
OptionParser.parse do |parser|
|
||||||
parser.on "-u file", "--passwd-file file", "passwd file." do |name|
|
parser.on "-u file", "--passwd-file file", "passwd file." do |name|
|
||||||
|
@ -129,6 +155,10 @@ OptionParser.parse do |parser|
|
||||||
authd_jwt_key = File.read(file_name).chomp
|
authd_jwt_key = File.read(file_name).chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
parser.on "-S dir", "--extra-storage dir", "Storage for extra user-data." do |directory|
|
||||||
|
authd_extra_storage = directory
|
||||||
|
end
|
||||||
|
|
||||||
parser.on "-R", "--allow-registrations" do
|
parser.on "-R", "--allow-registrations" do
|
||||||
authd_registrations = true
|
authd_registrations = true
|
||||||
end
|
end
|
||||||
|
@ -142,7 +172,7 @@ end
|
||||||
|
|
||||||
passwd = Passwd.new authd_passwd_file, authd_group_file
|
passwd = Passwd.new authd_passwd_file, authd_group_file
|
||||||
|
|
||||||
AuthD::Service.new(passwd, authd_jwt_key).tap do |authd|
|
AuthD::Service.new(passwd, authd_jwt_key, authd_extra_storage).tap do |authd|
|
||||||
authd.registrations_allowed = authd_registrations
|
authd.registrations_allowed = authd_registrations
|
||||||
end.run
|
end.run
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue