Major update that includes various breaking changes.
- backend is now a DODB::DataBase, not a passwd and group file anymore. - extras have been removed. A WIP User#profile field exists, that can be a JSON::Any. No profile validation has been implemented as of this commit. - authd now provides permission over resources, which is more precise than checking whether a user is part of a group. - permissions are now checked through authd once again: tokens don’t hold permissions anymore. - tokens are now minimal authentication “keys” to prove who you are and nothing more.ipc07
parent
6a947402d7
commit
b5c055b553
155
src/authd.cr
155
src/authd.cr
|
@ -6,6 +6,9 @@ require "ipc"
|
|||
|
||||
require "./user.cr"
|
||||
|
||||
class AuthD::Exception < Exception
|
||||
end
|
||||
|
||||
class AuthD::Response
|
||||
include JSON::Serializable
|
||||
|
||||
|
@ -49,13 +52,13 @@ class AuthD::Response
|
|||
end
|
||||
|
||||
class User < Response
|
||||
property user : Passwd::User
|
||||
property user : ::AuthD::User::Public
|
||||
|
||||
initialize :user
|
||||
end
|
||||
|
||||
class UserAdded < Response
|
||||
property user : Passwd::User
|
||||
property user : ::AuthD::User::Public
|
||||
|
||||
initialize :user
|
||||
end
|
||||
|
@ -66,28 +69,30 @@ class AuthD::Response
|
|||
initialize :uid
|
||||
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
|
||||
|
||||
class UsersList < Response
|
||||
property users : Array(Passwd::User)
|
||||
property users : Array(::AuthD::User::Public)
|
||||
|
||||
initialize :users
|
||||
end
|
||||
|
||||
class PermissionCheck < Response
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
|
||||
initialize :service, :resource, :user, :permission
|
||||
end
|
||||
|
||||
class PermissionSet < Response
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
|
||||
initialize :user, :service, :resource, :permission
|
||||
end
|
||||
|
||||
# This creates a Request::Type enumeration. One entry for each request type.
|
||||
{% begin %}
|
||||
enum Type
|
||||
|
@ -175,18 +180,15 @@ class AuthD::Request
|
|||
|
||||
property login : String
|
||||
property password : String
|
||||
property uid : Int32?
|
||||
property gid : Int32?
|
||||
property home : String?
|
||||
property shell : String?
|
||||
property profile : JSON::Any?
|
||||
|
||||
initialize :shared_key, :login, :password
|
||||
initialize :shared_key, :login, :password, :profile
|
||||
end
|
||||
|
||||
class GetUser < Request
|
||||
property uid : Int32
|
||||
property user : Int32 | String
|
||||
|
||||
initialize :uid
|
||||
initialize :user
|
||||
end
|
||||
|
||||
class GetUserByCredentials < Request
|
||||
|
@ -209,19 +211,9 @@ class AuthD::Request
|
|||
class Request::Register < Request
|
||||
property login : String
|
||||
property password : String
|
||||
property profile : JSON::Any?
|
||||
|
||||
initialize :login, :password
|
||||
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
|
||||
initialize :login, :password, :profile
|
||||
end
|
||||
|
||||
class Request::UpdatePassword < Request
|
||||
|
@ -235,6 +227,29 @@ class AuthD::Request
|
|||
property key : String?
|
||||
end
|
||||
|
||||
class CheckPermission < Request
|
||||
property shared_key : String
|
||||
|
||||
# FIXME: Make it Int32 | String
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
|
||||
initialize :shared_key, :user, :service, :resource
|
||||
end
|
||||
|
||||
class SetPermission < Request
|
||||
property shared_key : String
|
||||
|
||||
# FIXME: Make it Int32 | String
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
|
||||
initialize :shared_key, :user, :service, :resource, :permission
|
||||
end
|
||||
|
||||
# This creates a Request::Type enumeration. One entry for each request type.
|
||||
{% begin %}
|
||||
enum Type
|
||||
|
@ -315,13 +330,13 @@ module AuthD
|
|||
end
|
||||
end
|
||||
|
||||
def get_user?(uid : Int32)
|
||||
send Request::GetUser.new uid
|
||||
def get_user?(uid_or_login : Int32 | String) : ::AuthD::User::Public?
|
||||
send Request::GetUser.new uid_or_login
|
||||
|
||||
response = read
|
||||
response = Response.from_ipc read
|
||||
|
||||
if response.type == Response::Type::Ok.value.to_u8
|
||||
User.from_json String.new response.payload
|
||||
if response.is_a? Response::User
|
||||
response.user
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -334,14 +349,14 @@ module AuthD
|
|||
def decode_token(token)
|
||||
user, meta = JWT.decode token, @key, JWT::Algorithm::HS256
|
||||
|
||||
user = Passwd::User.from_json user.to_json
|
||||
user = ::AuthD::User::Public.from_json user.to_json
|
||||
|
||||
{user, meta}
|
||||
end
|
||||
|
||||
# FIXME: Extra options may be useful to implement here.
|
||||
def add_user(login : String, password : String) : Passwd::User | Exception
|
||||
send Request::AddUser.new @key, login, password
|
||||
def add_user(login : String, password : String, profile : JSON::Any?) : ::AuthD::User::Public | Exception
|
||||
send Request::AddUser.new @key, login, password, profile
|
||||
|
||||
response = Response.from_ipc read
|
||||
|
||||
|
@ -349,7 +364,7 @@ module AuthD
|
|||
when Response::UserAdded
|
||||
response.user
|
||||
when Response::Error
|
||||
Exception.new response.reason
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
# Should not happen in serialized connections, but…
|
||||
# it’ll happen if you run several requests at once.
|
||||
|
@ -357,6 +372,18 @@ module AuthD
|
|||
end
|
||||
end
|
||||
|
||||
def register(login : String, password : String, profile : JSON::Any?) : ::AuthD::User::Public?
|
||||
send Request::Register.new login, password, profile
|
||||
|
||||
response = Response.from_ipc read
|
||||
|
||||
case response
|
||||
when Response::UserAdded
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
end
|
||||
end
|
||||
|
||||
def mod_user(uid : Int32, password : String? = nil, avatar : String? = nil) : Bool | Exception
|
||||
request = Request::ModUser.new @key, uid
|
||||
|
||||
|
@ -376,6 +403,40 @@ module AuthD
|
|||
Exception.new "???"
|
||||
end
|
||||
end
|
||||
|
||||
def check_permission(user : ::AuthD::User::Public, service_name : String, resource_name : String) : User::PermissionLevel
|
||||
request = Request::CheckPermission.new @key, user.uid, service_name, resource_name
|
||||
|
||||
send request
|
||||
|
||||
response = Response.from_ipc read
|
||||
|
||||
case response
|
||||
when Response::PermissionCheck
|
||||
response.permission
|
||||
when Response
|
||||
raise Exception.new "unexpected response: #{response.type}"
|
||||
else
|
||||
raise Exception.new "unexpected response"
|
||||
end
|
||||
end
|
||||
|
||||
def set_permission(uid : Int32, service : String, resource : String, permission : User::PermissionLevel)
|
||||
request = Request::SetPermission.new @key, uid, service, resource, permission
|
||||
|
||||
send request
|
||||
|
||||
response = Response.from_ipc read
|
||||
|
||||
case response
|
||||
when Response::PermissionSet
|
||||
true
|
||||
when Response
|
||||
raise Exception.new "unexpected response: #{response.type}"
|
||||
else
|
||||
raise Exception.new "unexpected response"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
223
src/main.cr
223
src/main.cr
|
@ -3,7 +3,6 @@ require "option_parser"
|
|||
require "openssl"
|
||||
|
||||
require "jwt"
|
||||
require "passwd"
|
||||
require "ipc"
|
||||
require "dodb"
|
||||
|
||||
|
@ -14,59 +13,114 @@ extend AuthD
|
|||
class AuthD::Service
|
||||
property registrations_allowed = false
|
||||
|
||||
def initialize(@passwd : Passwd, @jwt_key : String, @extras_root : String)
|
||||
@users_per_login : DODB::Index(User)
|
||||
|
||||
def initialize(@storage_root : String, @jwt_key : String)
|
||||
@users = DODB::DataBase(String, User).new @storage_root
|
||||
@users_per_login = @users.new_index "login", &.login
|
||||
|
||||
@last_uid_file = "#{@storage_root}/last_used_uid"
|
||||
end
|
||||
|
||||
def hash_password(password : String) : String
|
||||
digest = OpenSSL::Digest.new "sha256"
|
||||
digest << password
|
||||
digest.hexdigest
|
||||
end
|
||||
|
||||
def new_uid
|
||||
begin
|
||||
uid = File.read(@last_uid_file).to_i
|
||||
rescue
|
||||
uid = 999
|
||||
end
|
||||
|
||||
uid += 1
|
||||
|
||||
File.write @last_uid_file, uid.to_s
|
||||
|
||||
uid
|
||||
end
|
||||
|
||||
def handle_request(request : AuthD::Request?, connection : IPC::Connection)
|
||||
case request
|
||||
when Request::GetToken
|
||||
user = @passwd.get_user request.login, request.password
|
||||
user = @users_per_login.get request.login
|
||||
|
||||
if user.password_hash != hash_password request.password
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
token = JWT.encode user.to_h, @jwt_key, JWT::Algorithm::HS256
|
||||
token = user.to_token
|
||||
|
||||
Response::Token.new token
|
||||
Response::Token.new token.to_s @jwt_key
|
||||
when Request::AddUser
|
||||
if request.shared_key != @jwt_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
if @passwd.user_exists? request.login
|
||||
if @users_per_login.get? request.login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
user = @passwd.add_user request.login, request.password
|
||||
password_hash = hash_password request.password
|
||||
|
||||
Response::UserAdded.new user
|
||||
uid = new_uid
|
||||
|
||||
user = User.new uid, request.login, password_hash
|
||||
|
||||
request.profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
@users[user.uid.to_s] = user
|
||||
|
||||
Response::UserAdded.new user.to_public
|
||||
when Request::GetUserByCredentials
|
||||
user = @passwd.get_user request.login, request.password
|
||||
user = @users_per_login.get request.login
|
||||
|
||||
if user
|
||||
Response::User.new user
|
||||
else
|
||||
Response::Error.new "user not found"
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if hash_password(request.password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
Response::User.new user.to_public
|
||||
when Request::GetUser
|
||||
user = @passwd.get_user request.uid
|
||||
|
||||
if user
|
||||
Response::User.new user
|
||||
uid_or_login = request.user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
@users[uid_or_login.to_s]?
|
||||
else
|
||||
Response::Error.new "user not found"
|
||||
@users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
Response::User.new user.to_public
|
||||
when Request::ModUser
|
||||
if request.shared_key != @jwt_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
password_hash = request.password.try do |s|
|
||||
Passwd.hash_password s
|
||||
user = @users[request.uid.to_s]?
|
||||
|
||||
unless user
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
@passwd.mod_user request.uid, password_hash: password_hash
|
||||
password_hash = request.password.try do |s|
|
||||
user.password_hash = hash_password s
|
||||
end
|
||||
|
||||
@users[user.uid.to_s] = user
|
||||
|
||||
Response::UserEdited.new request.uid
|
||||
when Request::Register
|
||||
|
@ -74,48 +128,46 @@ class AuthD::Service
|
|||
return Response::Error.new "registrations not allowed"
|
||||
end
|
||||
|
||||
if @passwd.user_exists? request.login
|
||||
if @users_per_login.get? request.login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
user = @passwd.add_user request.login, request.password
|
||||
uid = new_uid
|
||||
password = hash_password request.password
|
||||
|
||||
Response::UserAdded.new user
|
||||
when Request::GetExtra
|
||||
user = get_user_from_token request.token
|
||||
user = User.new uid, request.login, password
|
||||
|
||||
return Response::Error.new "invalid token" unless user
|
||||
request.profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
storage = DODB::DataBase(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
|
||||
@users[user.uid.to_s] = user
|
||||
|
||||
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 = DODB::DataBase(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
|
||||
|
||||
storage[request.name] = request.extra
|
||||
|
||||
Response::ExtraUpdated.new user.uid, request.name, request.extra
|
||||
Response::UserAdded.new user.to_public
|
||||
when Request::UpdatePassword
|
||||
user = @passwd.get_user request.login, request.old_password
|
||||
user = @users_per_login.get? request.login
|
||||
|
||||
return Response::Error.new "invalid credentials" unless user
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
password_hash = Passwd.hash_password request.new_password
|
||||
if hash_password(request.old_password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
@passwd.mod_user user.uid, password_hash: password_hash
|
||||
user.password_hash = hash_password request.new_password
|
||||
|
||||
@users[user.uid.to_s] = user
|
||||
|
||||
Response::UserEdited.new user.uid
|
||||
when Request::ListUsers
|
||||
# FIXME: Lines too long, repeatedly (>80c with 4c tabs).
|
||||
request.token.try do |token|
|
||||
user = get_user_from_token token
|
||||
|
||||
return Response::Error.new "unauthorized (user not found from token)" unless user
|
||||
return Response::Error.new "unauthorized (user not found from token)"
|
||||
|
||||
return Response::Error.new "unauthorized (user not in authd group)" unless user.groups.any? &.==("authd")
|
||||
return Response::Error.new "unauthorized (user not in authd group)" unless user.permissions["authd"]?.try(&.["*"].>=(User::PermissionLevel::Read))
|
||||
end
|
||||
|
||||
request.key.try do |key|
|
||||
|
@ -124,16 +176,69 @@ class AuthD::Service
|
|||
|
||||
return Response::Error.new "unauthorized (no key nor token)" unless request.key || request.token
|
||||
|
||||
Response::UsersList.new @passwd.get_all_users
|
||||
Response::UsersList.new @users.to_h.map &.[1].to_public
|
||||
when Request::CheckPermission
|
||||
unless request.shared_key == @jwt_key
|
||||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = @users[request.user.to_s]?
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = request.service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
resource_permissions = service_permissions[request.resource]?
|
||||
|
||||
if resource_permissions.nil?
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, resource_permissions
|
||||
when Request::SetPermission
|
||||
unless request.shared_key == @jwt_key
|
||||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = @users[request.user.to_s]?
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = request.service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
service_permissions = Hash(String, User::PermissionLevel).new
|
||||
user.permissions[service] = service_permissions
|
||||
end
|
||||
|
||||
if request.permission.none?
|
||||
service_permissions.delete request.resource
|
||||
else
|
||||
service_permissions[request.resource] = request.permission
|
||||
end
|
||||
|
||||
@users[user.uid.to_s] = user
|
||||
|
||||
Response::PermissionSet.new user.uid, service, request.resource, request.permission
|
||||
else
|
||||
Response::Error.new "unhandled request type"
|
||||
end
|
||||
end
|
||||
|
||||
def get_user_from_token(token)
|
||||
user, meta = JWT.decode token, @jwt_key, JWT::Algorithm::HS256
|
||||
def get_user_from_token(token : String)
|
||||
token_payload = Token.from_s(token, @jwt_key)
|
||||
|
||||
Passwd::User.from_json user.to_json
|
||||
@users[token_payload.uid.to_s]?
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -162,29 +267,19 @@ class AuthD::Service
|
|||
end
|
||||
end
|
||||
|
||||
authd_passwd_file = "passwd"
|
||||
authd_group_file = "group"
|
||||
authd_storage = "storage"
|
||||
authd_jwt_key = "nico-nico-nii"
|
||||
authd_registrations = false
|
||||
authd_extra_storage = "storage"
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.on "-u file", "--passwd-file file", "passwd file." do |name|
|
||||
authd_passwd_file = name
|
||||
end
|
||||
|
||||
parser.on "-g file", "--group-file file", "group file." do |name|
|
||||
authd_group_file = name
|
||||
parser.on "-s directory", "--storage directory", "Directory in which to store users." do |directory|
|
||||
authd_storage = directory
|
||||
end
|
||||
|
||||
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
|
||||
authd_jwt_key = File.read(file_name).chomp
|
||||
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
|
||||
authd_registrations = true
|
||||
end
|
||||
|
@ -196,9 +291,7 @@ OptionParser.parse do |parser|
|
|||
end
|
||||
end
|
||||
|
||||
passwd = Passwd.new authd_passwd_file, authd_group_file
|
||||
|
||||
AuthD::Service.new(passwd, authd_jwt_key, authd_extra_storage).tap do |authd|
|
||||
AuthD::Service.new(authd_storage, authd_jwt_key).tap do |authd|
|
||||
authd.registrations_allowed = authd_registrations
|
||||
end.run
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
require "json"
|
||||
|
||||
class AuthD::User
|
||||
include JSON::Serializable
|
||||
|
||||
property login : String
|
||||
property password_hash : String?
|
||||
property uid : Int32
|
||||
|
||||
property mail_address : String
|
||||
|
||||
# FIXME: How would this profile be extended, replaced, checked?
|
||||
property profile : Profile
|
||||
|
||||
class Profile
|
||||
include JSON::Serializable
|
||||
|
||||
property full_name : String?
|
||||
property description : String?
|
||||
property avatar : String?
|
||||
property website : String?
|
||||
end
|
||||
|
||||
property registration_date : Time
|
||||
|
||||
property groups : Array(String)
|
||||
|
||||
# application name => configuration object
|
||||
property configuration : Hash(String, JSON::Any)
|
||||
end
|
||||
|
||||
class AuthD::Group
|
||||
include JSON::Serializable
|
||||
|
||||
property name : String
|
||||
property gid : Int32
|
||||
property members : Array(String)
|
||||
end
|
||||
|
||||
class AuthD::Storage
|
||||
# FIXME: Create new groups and users, generate their ids.
|
||||
def initialize(@storage_root)
|
||||
@users = DODB::Hash(Int32, User).new "#{@storage_root}/users"
|
||||
@users_by_login = @users.new_index "login", &.login
|
||||
@users_by_group = @users.new_tags "groups", &.groups
|
||||
|
||||
@groups = DODB::Hash(Int32, Group).new "#{@storage_root}/groups"
|
||||
@groups_by_name = new_index "name", &.name
|
||||
@groups_by_member = new_tags "members", &.members
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
require "json"
|
||||
|
||||
class AuthD::Token
|
||||
include JSON::Serializable
|
||||
|
||||
property login : String
|
||||
property uid : Int32
|
||||
|
||||
def initialize(@login, @uid)
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
:login => login,
|
||||
:uid => uid
|
||||
}
|
||||
end
|
||||
|
||||
def to_s(key)
|
||||
JWT.encode to_h.to_json, key, JWT::Algorithm::HS256
|
||||
end
|
||||
|
||||
def self.from_s(key, str)
|
||||
payload, meta = JWT.decode str, key, JWT::Algorithm::HS256
|
||||
puts "PAYLOAD BELOW, BEWARE"
|
||||
pp! payload
|
||||
|
||||
self.new payload["login"].as_s, payload["uid"].as_i
|
||||
end
|
||||
end
|
||||
|
76
src/user.cr
76
src/user.cr
|
@ -1,41 +1,53 @@
|
|||
require "json"
|
||||
|
||||
require "passwd"
|
||||
require "./token.cr"
|
||||
|
||||
class Passwd::User
|
||||
JSON.mapping({
|
||||
login: String,
|
||||
password_hash: String,
|
||||
uid: Int32,
|
||||
gid: Int32,
|
||||
home: String,
|
||||
shell: String,
|
||||
groups: Array(String),
|
||||
full_name: String?,
|
||||
office_phone_number: String?,
|
||||
home_phone_number: String?,
|
||||
other_contact: String?,
|
||||
})
|
||||
class AuthD::User
|
||||
include JSON::Serializable
|
||||
|
||||
def sanitize!
|
||||
@password_hash = "x"
|
||||
self
|
||||
enum PermissionLevel
|
||||
None
|
||||
Read
|
||||
Edit
|
||||
Admin
|
||||
|
||||
def to_json(o)
|
||||
to_s.downcase.to_json o
|
||||
end
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
:login => @login,
|
||||
:password_hash => "x", # Not real hash in JWT.
|
||||
:uid => @uid,
|
||||
:gid => @gid,
|
||||
:home => @home,
|
||||
:shell => @shell,
|
||||
:groups => @groups,
|
||||
:full_name => @full_name,
|
||||
:office_phone_number => @office_phone_number,
|
||||
:home_phone_number => @home_phone_number,
|
||||
:other_contact => @other_contact
|
||||
}
|
||||
# Public.
|
||||
property login : String
|
||||
property uid : Int32
|
||||
property profile : JSON::Any?
|
||||
|
||||
# Private.
|
||||
property password_hash : String
|
||||
property permissions : Hash(String, Hash(String, PermissionLevel))
|
||||
property configuration : Hash(String, Hash(String, JSON::Any))
|
||||
|
||||
def to_token
|
||||
Token.new @login, @uid
|
||||
end
|
||||
|
||||
def initialize(@uid, @login, @password_hash)
|
||||
@permissions = Hash(String, Hash(String, PermissionLevel)).new
|
||||
@configuration = Hash(String, Hash(String, JSON::Any)).new
|
||||
end
|
||||
|
||||
class Public
|
||||
include JSON::Serializable
|
||||
|
||||
property login : String
|
||||
property uid : Int32
|
||||
property profile : JSON::Any?
|
||||
|
||||
def initialize(@uid, @login, @profile)
|
||||
end
|
||||
end
|
||||
|
||||
def to_public : Public
|
||||
Public.new @uid, @login, @profile
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue