commit
58e8bd28a6
|
@ -11,8 +11,6 @@ description: |
|
||||||
targets:
|
targets:
|
||||||
authd:
|
authd:
|
||||||
main: src/main.cr
|
main: src/main.cr
|
||||||
authd-adduser:
|
|
||||||
main: src/adduser.cr
|
|
||||||
|
|
||||||
crystal: 0.27
|
crystal: 0.27
|
||||||
|
|
||||||
|
@ -23,9 +21,5 @@ dependencies:
|
||||||
jwt:
|
jwt:
|
||||||
github: crystal-community/jwt
|
github: crystal-community/jwt
|
||||||
branch: master
|
branch: master
|
||||||
pg:
|
|
||||||
github: will/crystal-pg
|
|
||||||
crecto:
|
|
||||||
github: fridgerator/crecto
|
|
||||||
|
|
||||||
license: EUPL
|
license: EUPL
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
|
|
||||||
require "option_parser"
|
|
||||||
|
|
||||||
require "./user.cr"
|
|
||||||
|
|
||||||
user_name : String? = nil
|
|
||||||
user_password : String? = nil
|
|
||||||
user_perms = Array(String).new
|
|
||||||
|
|
||||||
database_url = "postgres:localhost"
|
|
||||||
database_name = "authd"
|
|
||||||
database_user = "nico"
|
|
||||||
database_password = "nico-nico-nii"
|
|
||||||
|
|
||||||
OptionParser.parse! do |parser|
|
|
||||||
parser.banner = "usage: #{ARGV[0]} [arguments]"
|
|
||||||
|
|
||||||
parser.on "-a url", "--database url", "URL of authd’s database." do |url|
|
|
||||||
database_url = url
|
|
||||||
end
|
|
||||||
parser.on "-U name", "--database-user name", "Name of the database’s user." do |name|
|
|
||||||
database_user = name
|
|
||||||
end
|
|
||||||
parser.on "-P password", "--database-pasword password", "Password of the database’s user." do |password|
|
|
||||||
database_password = password
|
|
||||||
end
|
|
||||||
parser.on "-N name", "--database-name name", "Name of the database." do |name|
|
|
||||||
database_name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
parser.on "-u name", "--username name", "Name of the user to add." do |name|
|
|
||||||
user_name = name
|
|
||||||
end
|
|
||||||
parser.on "-p password", "--password password", "Password of the user." do |password|
|
|
||||||
user_password = password
|
|
||||||
end
|
|
||||||
parser.on "-x perm", "--permission", "Add a permission token to the user." do |token|
|
|
||||||
user_perms << token
|
|
||||||
end
|
|
||||||
|
|
||||||
parser.on("-h", "--help", "Show this help") do
|
|
||||||
puts parser
|
|
||||||
exit 0
|
|
||||||
end
|
|
||||||
|
|
||||||
parser.invalid_option do |flag|
|
|
||||||
puts "error: #{flag} is not a valid option."
|
|
||||||
puts parser
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module DataBase
|
|
||||||
extend Crecto::Repo
|
|
||||||
end
|
|
||||||
|
|
||||||
DataBase.config do |conf|
|
|
||||||
conf.adapter = Crecto::Adapters::Postgres
|
|
||||||
conf.hostname = database_url
|
|
||||||
conf.username = database_user
|
|
||||||
conf.password = database_password
|
|
||||||
conf.database = database_name
|
|
||||||
end
|
|
||||||
|
|
||||||
if user_name.nil?
|
|
||||||
STDERR.puts "No username specified."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
if user_password.nil?
|
|
||||||
STDERR.puts "No user password specified."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
user = AuthD::User.new
|
|
||||||
user.username = user_name
|
|
||||||
user.password = user_password
|
|
||||||
user.perms = user_perms
|
|
||||||
changeset = DataBase.insert user
|
|
||||||
|
|
||||||
if changeset.valid?
|
|
||||||
puts "User added successfully."
|
|
||||||
else
|
|
||||||
changeset.errors.each do |e|
|
|
||||||
p e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
55
src/authd.cr
55
src/authd.cr
|
@ -4,25 +4,40 @@ require "jwt"
|
||||||
require "ipc"
|
require "ipc"
|
||||||
|
|
||||||
require "./user.cr"
|
require "./user.cr"
|
||||||
|
require "./group.cr"
|
||||||
|
|
||||||
module AuthD
|
module AuthD
|
||||||
enum RequestTypes
|
enum RequestTypes
|
||||||
GET_TOKEN
|
GetToken
|
||||||
|
AddUser
|
||||||
end
|
end
|
||||||
|
|
||||||
enum ResponseTypes
|
enum ResponseTypes
|
||||||
OK
|
Ok
|
||||||
MALFORMED_REQUEST
|
MalformedRequest
|
||||||
INVALID_CREDENTIALS
|
InvalidCredentials
|
||||||
|
InvalidUser
|
||||||
end
|
end
|
||||||
|
|
||||||
class GetTokenRequest
|
class GetTokenRequest
|
||||||
JSON.mapping({
|
JSON.mapping({
|
||||||
username: String,
|
# FIXME: Rename to "login" for consistency.
|
||||||
|
login: String,
|
||||||
password: String
|
password: String
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class AddUserRequest
|
||||||
|
JSON.mapping({
|
||||||
|
login: String,
|
||||||
|
password: String,
|
||||||
|
uid: Int32?,
|
||||||
|
gid: Int32?,
|
||||||
|
home: String?,
|
||||||
|
shell: String?
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
class Client < IPC::Client
|
class Client < IPC::Client
|
||||||
property key : String
|
property key : String
|
||||||
|
|
||||||
|
@ -32,21 +47,25 @@ module AuthD
|
||||||
initialize "auth"
|
initialize "auth"
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_token?(username : String, password : String)
|
def get_token?(login : String, password : String)
|
||||||
send RequestTypes::GET_TOKEN.value.to_u8, {
|
send RequestTypes::GetToken.value.to_u8, {
|
||||||
:username => username,
|
:login => login,
|
||||||
:password => password
|
:password => password
|
||||||
}.to_json
|
}.to_json
|
||||||
|
|
||||||
response = read
|
response = read
|
||||||
|
|
||||||
if response.type == ResponseTypes::OK.value.to_u8
|
if response.type == ResponseTypes::Ok.value.to_u8
|
||||||
response.payload
|
response.payload
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send(type : RequestTypes, payload)
|
||||||
|
send type.value.to_u8, payload
|
||||||
|
end
|
||||||
|
|
||||||
def decode_token(token)
|
def decode_token(token)
|
||||||
user, meta = JWT.decode token, @key, "HS256"
|
user, meta = JWT.decode token, @key, "HS256"
|
||||||
|
|
||||||
|
@ -54,6 +73,24 @@ module AuthD
|
||||||
|
|
||||||
{user, meta}
|
{user, meta}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# FIXME: Extra options may be useful to implement here.
|
||||||
|
def add_user(login : String, password : String) : AuthD::User | Exception
|
||||||
|
send RequestTypes::AddUser, {
|
||||||
|
:login => login,
|
||||||
|
:password => password
|
||||||
|
}.to_json
|
||||||
|
|
||||||
|
response = read
|
||||||
|
|
||||||
|
pp! response.type
|
||||||
|
case ResponseTypes.new response.type.to_i
|
||||||
|
when ResponseTypes::Ok
|
||||||
|
AuthD::User.from_json response.payload
|
||||||
|
else
|
||||||
|
Exception.new response.payload
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
class AuthD::Group
|
||||||
|
getter name : String
|
||||||
|
getter password_hash : String
|
||||||
|
getter gid : Int32
|
||||||
|
getter users = Array(String).new
|
||||||
|
|
||||||
|
def initialize(@name, @password_hash, @gid, @users)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
80
src/main.cr
80
src/main.cr
|
@ -9,30 +9,27 @@ require "crecto"
|
||||||
require "ipc"
|
require "ipc"
|
||||||
|
|
||||||
require "./authd.cr"
|
require "./authd.cr"
|
||||||
|
require "./passwd.cr"
|
||||||
|
|
||||||
extend AuthD
|
extend AuthD
|
||||||
|
|
||||||
authd_db_name = "authd"
|
class IPC::RemoteClient
|
||||||
authd_db_hostname = "localhost"
|
def send(type : ResponseTypes, payload : String)
|
||||||
authd_db_user = "user"
|
send type.value.to_u8, payload
|
||||||
authd_db_password = "nico-nico-nii"
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
authd_passwd_file = "passwd"
|
||||||
|
authd_group_file = "group"
|
||||||
authd_jwt_key = "nico-nico-nii"
|
authd_jwt_key = "nico-nico-nii"
|
||||||
|
|
||||||
OptionParser.parse! do |parser|
|
OptionParser.parse! do |parser|
|
||||||
parser.on "-d name", "--database-name name", "Database name." do |name|
|
parser.on "-u file", "--passwd-file file", "passwd file." do |name|
|
||||||
authd_db_name = name
|
authd_passwd_file = name
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.on "-u name", "--database-username user", "Database user." do |name|
|
parser.on "-g file", "--group-file file", "group file." do |name|
|
||||||
authd_db_user = name
|
authd_group_file = name
|
||||||
end
|
|
||||||
|
|
||||||
parser.on "-a host", "--hostname host", "Database host name." do |host|
|
|
||||||
authd_db_hostname = host
|
|
||||||
end
|
|
||||||
|
|
||||||
parser.on "-P file", "--password-file file", "Password file." do |file_name|
|
|
||||||
authd_db_password = File.read(file_name).chomp
|
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
|
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
|
||||||
|
@ -46,26 +43,7 @@ OptionParser.parse! do |parser|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module DataBase
|
passwd = Passwd.new authd_passwd_file, authd_group_file
|
||||||
extend Crecto::Repo
|
|
||||||
end
|
|
||||||
|
|
||||||
DataBase.config do |conf|
|
|
||||||
conf.adapter = Crecto::Adapters::Postgres
|
|
||||||
conf.hostname = authd_db_hostname
|
|
||||||
conf.database = authd_db_name
|
|
||||||
conf.username = authd_db_user
|
|
||||||
conf.password = authd_db_password
|
|
||||||
end
|
|
||||||
|
|
||||||
# Dummy query to check DB connection is possible.
|
|
||||||
begin
|
|
||||||
DataBase.all User, Crecto::Repo::Query.new
|
|
||||||
rescue e
|
|
||||||
puts "Database connection failed: #{e.message}"
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Provides a JWT-based authentication scheme for service-specific users.
|
# Provides a JWT-based authentication scheme for service-specific users.
|
||||||
|
@ -78,27 +56,43 @@ IPC::Service.new "auth" do |event|
|
||||||
payload = message.payload
|
payload = message.payload
|
||||||
|
|
||||||
case RequestTypes.new message.type.to_i
|
case RequestTypes.new message.type.to_i
|
||||||
when RequestTypes::GET_TOKEN
|
when RequestTypes::GetToken
|
||||||
begin
|
begin
|
||||||
request = GetTokenRequest.from_json payload
|
request = GetTokenRequest.from_json payload
|
||||||
rescue e
|
rescue e
|
||||||
client.send ResponseTypes::MALFORMED_REQUEST.value.to_u8, e.message || ""
|
client.send ResponseTypes::MalformedRequest.value.to_u8, e.message || ""
|
||||||
|
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
user = DataBase.get_by User,
|
user = passwd.get_user request.login, request.password
|
||||||
username: request.username,
|
|
||||||
password: request.password
|
|
||||||
|
|
||||||
if user.nil?
|
if user.nil?
|
||||||
client.send ResponseTypes::INVALID_CREDENTIALS.value.to_u8, ""
|
client.send ResponseTypes::InvalidCredentials.value.to_u8, ""
|
||||||
|
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
client.send ResponseTypes::OK.value.to_u8,
|
client.send ResponseTypes::Ok.value.to_u8,
|
||||||
JWT.encode user.to_h, authd_jwt_key, "HS256"
|
JWT.encode user.to_h, authd_jwt_key, "HS256"
|
||||||
|
when RequestTypes::AddUser
|
||||||
|
begin
|
||||||
|
request = AddUserRequest.from_json payload
|
||||||
|
rescue e
|
||||||
|
client.send ResponseTypes::MalformedRequest.value.to_u8, e.message || ""
|
||||||
|
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if passwd.user_exists? request.login
|
||||||
|
client.send ResponseTypes::InvalidUser, "Another user with the same login already exists."
|
||||||
|
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
user = passwd.add_user request.login, request.password
|
||||||
|
|
||||||
|
client.send ResponseTypes::Ok, user.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
require "csv"
|
||||||
|
require "uuid"
|
||||||
|
|
||||||
|
require "./user.cr"
|
||||||
|
require "./group.cr"
|
||||||
|
|
||||||
|
# FIXME: Should we work on arrays and convert to CSV at the last second when adding rows?
|
||||||
|
|
||||||
|
class Passwd
|
||||||
|
@passwd : String
|
||||||
|
@group : String
|
||||||
|
|
||||||
|
# FIXME: Missing operations:
|
||||||
|
# - Removing users.
|
||||||
|
# - Reading groups.
|
||||||
|
# - Adding and removing groups (ok, maybe admins can do that?)
|
||||||
|
# - Adding users to group.
|
||||||
|
# - Removing users from group.
|
||||||
|
|
||||||
|
# FIXME: Safety. Ensure passwd and group cannot be in a half-written state. (backups will probably work well enough in the mean-time)
|
||||||
|
|
||||||
|
def initialize(@passwd, @group)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def passwd_as_array
|
||||||
|
CSV.parse File.read(@passwd), separator: ':'
|
||||||
|
end
|
||||||
|
|
||||||
|
private def group_as_array
|
||||||
|
CSV.parse File.read(@group), separator: ':'
|
||||||
|
end
|
||||||
|
|
||||||
|
private def set_user_groups(user : AuthD::User)
|
||||||
|
group_as_array.each do |line|
|
||||||
|
group = AuthD::Group.new line
|
||||||
|
|
||||||
|
if group.users.any? { |name| name == user.login }
|
||||||
|
user.groups << group.name
|
||||||
|
end
|
||||||
|
|
||||||
|
pp group
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_user(&block)
|
||||||
|
passwd_as_array.each do |line|
|
||||||
|
yield AuthD::User.new line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_exists?(login : String) : Bool
|
||||||
|
each_user do |user|
|
||||||
|
return true if user.login == login
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_user(uid : Int32) : AuthD::User?
|
||||||
|
each_user do |user|
|
||||||
|
if user.uid == uid
|
||||||
|
# FIXME: Check user groups and register them here.
|
||||||
|
set_user_groups user
|
||||||
|
|
||||||
|
return user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Will fail if the user is found but the password is invalid.
|
||||||
|
def get_user(login : String, password : String) : AuthD::User?
|
||||||
|
digest = OpenSSL::Digest.new("sha256")
|
||||||
|
digest << password
|
||||||
|
hash = digest.hexdigest
|
||||||
|
|
||||||
|
each_user do |user|
|
||||||
|
if user.login == login
|
||||||
|
# FIXME: XXX: HASH!!!!!
|
||||||
|
if user.password_hash == hash
|
||||||
|
return user
|
||||||
|
end
|
||||||
|
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all_users
|
||||||
|
users = Array(AuthD::User).new
|
||||||
|
|
||||||
|
passwd_as_array.each do |line|
|
||||||
|
users << AuthD::User.new line
|
||||||
|
end
|
||||||
|
|
||||||
|
users
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all_groups
|
||||||
|
groups = Array(AuthD::Group).new
|
||||||
|
|
||||||
|
group_as_array.each do |line|
|
||||||
|
groups << AuthD::Group.new line
|
||||||
|
end
|
||||||
|
|
||||||
|
groups
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_passwd
|
||||||
|
get_all_users.map(&.to_csv).join("\n") + "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_group
|
||||||
|
get_all_groups.map(&.to_csv).join("\n") + "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
private def get_free_uid
|
||||||
|
uid = 1000
|
||||||
|
|
||||||
|
users = get_all_users
|
||||||
|
|
||||||
|
while users.any? &.uid.== uid
|
||||||
|
uid += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
uid
|
||||||
|
end
|
||||||
|
|
||||||
|
private def get_free_gid
|
||||||
|
gid = 1000
|
||||||
|
|
||||||
|
users = get_all_users
|
||||||
|
|
||||||
|
while users.any? &.gid.== gid
|
||||||
|
gid += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
gid
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_user(login, password = nil, uid = nil, gid = nil, home = "/", shell = "/bin/nologin")
|
||||||
|
# FIXME: If user already exists, exception? Replacement?
|
||||||
|
|
||||||
|
uid = get_free_uid if uid.nil?
|
||||||
|
|
||||||
|
gid = get_free_gid if gid.nil?
|
||||||
|
|
||||||
|
password_hash = if password
|
||||||
|
digest = OpenSSL::Digest.new("sha256")
|
||||||
|
digest << password
|
||||||
|
digest.hexdigest
|
||||||
|
else
|
||||||
|
"x"
|
||||||
|
end
|
||||||
|
|
||||||
|
user = AuthD::User.new login, password_hash, uid, gid, home, shell
|
||||||
|
|
||||||
|
File.write(@passwd, user.to_csv + "\n", mode: "a")
|
||||||
|
|
||||||
|
add_group login, gid: gid, users: [user.login]
|
||||||
|
|
||||||
|
set_user_groups user
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_group(name, password_hash = "x", gid = nil, users = Array(String).new)
|
||||||
|
gid = get_free_gid if gid.nil?
|
||||||
|
|
||||||
|
group = AuthD::Group.new name, password_hash, gid, users
|
||||||
|
|
||||||
|
File.write(@group, group.to_csv + "\n", mode: "a")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthD::Group
|
||||||
|
def initialize(line : Array(String))
|
||||||
|
@name = line[0]
|
||||||
|
@password_hash = line[1]
|
||||||
|
@gid = line[2].to_i
|
||||||
|
|
||||||
|
@users = line[3].split ","
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_csv
|
||||||
|
[@name, @password_hash, @gid, @users.join ","].join ":"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthD::User
|
||||||
|
def initialize(line : Array(String))
|
||||||
|
@login = line[0]
|
||||||
|
@password_hash = line[1]
|
||||||
|
|
||||||
|
@uid = line[2].to_i
|
||||||
|
@gid = line[3].to_i
|
||||||
|
|
||||||
|
CSV.parse(line[4], separator: ',')[0]?.try do |gecos|
|
||||||
|
@full_name = gecos[0]?
|
||||||
|
@location = gecos[1]?
|
||||||
|
@office_phone_number = gecos[2]?
|
||||||
|
@home_phone_number = gecos[3]?
|
||||||
|
@other_contact = gecos[4]?
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: What about those two fields? Keep them, remove them?
|
||||||
|
@home = line[5]
|
||||||
|
@shell = line[6]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_csv
|
||||||
|
[@login, @password_hash, @uid, @gid, gecos, @home, @shell].join ":"
|
||||||
|
end
|
||||||
|
|
||||||
|
def gecos
|
||||||
|
unless @location || @office_phone_number || @home_phone_number || @other_contact
|
||||||
|
if @full_name
|
||||||
|
return @full_name
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[@full_name || "", @location || "", @office_phone_number || "", @home_phone_number || "", @other_contact || ""].join ","
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
57
src/user.cr
57
src/user.cr
|
@ -1,24 +1,51 @@
|
||||||
|
|
||||||
require "pg"
|
class AuthD::User
|
||||||
require "crecto"
|
getter login : String
|
||||||
|
getter password_hash : String
|
||||||
|
getter uid : Int32
|
||||||
|
getter gid : Int32
|
||||||
|
getter home : String = "/"
|
||||||
|
getter shell : String = "/bin/nologin"
|
||||||
|
getter groups = Array(String).new
|
||||||
|
getter full_name : String? = nil
|
||||||
|
getter avatar : String? = nil
|
||||||
|
getter location : String? = nil
|
||||||
|
getter office_phone_number : String? = nil
|
||||||
|
getter home_phone_number : String? = nil
|
||||||
|
getter other_contact : String? = nil
|
||||||
|
|
||||||
class AuthD::User < Crecto::Model
|
JSON.mapping({
|
||||||
schema "users" do # table name
|
login: String,
|
||||||
field :username, String
|
password_hash: String,
|
||||||
field :realname, String
|
uid: Int32,
|
||||||
field :avatar, String
|
gid: Int32,
|
||||||
field :password, String
|
home: String,
|
||||||
field :perms, Array(String)
|
shell: String,
|
||||||
|
groups: Array(String),
|
||||||
|
full_name: String?,
|
||||||
|
avatar: String?,
|
||||||
|
office_phone_number: String?,
|
||||||
|
home_phone_number: String?,
|
||||||
|
other_contact: String?
|
||||||
|
})
|
||||||
|
|
||||||
|
def initialize(@login, @password_hash, @uid, @gid, @home, @shell)
|
||||||
end
|
end
|
||||||
|
|
||||||
validate_required [:username, :password, :perms]
|
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
{
|
{
|
||||||
:username => @username,
|
:login => @login,
|
||||||
:realname => @realname,
|
:password_hash => "x", # Not real hash in JWT.
|
||||||
:perms => @perms,
|
:uid => @uid,
|
||||||
:avatar => @avatar
|
:gid => @gid,
|
||||||
|
:home => @home,
|
||||||
|
:shell => @shell,
|
||||||
|
:groups => @groups,
|
||||||
|
:full_name => @full_name,
|
||||||
|
:avatar => @avatar,
|
||||||
|
:office_phone_number => @office_phone_number,
|
||||||
|
:home_phone_number => @home_phone_number,
|
||||||
|
:other_contact => @other_contact
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue