Compiles.
parent
6a6ccfe37f
commit
3879e7915d
11
src/authd.cr
11
src/authd.cr
|
@ -13,8 +13,11 @@ class Baguette::Configuration
|
|||
|
||||
property login : String? = nil
|
||||
property pass : String? = nil
|
||||
property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes:
|
||||
property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes:
|
||||
property shared_key_file : String? = nil
|
||||
|
||||
def initialize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -22,6 +25,12 @@ module AuthD
|
|||
class Exception < ::Exception
|
||||
end
|
||||
|
||||
class UserNotFound < ::Exception
|
||||
end
|
||||
|
||||
class AuthenticationInfoLacking < ::Exception
|
||||
end
|
||||
|
||||
# Not used for now.
|
||||
class NotLoggedException < ::Exception
|
||||
end
|
||||
|
|
19
src/main.cr
19
src/main.cr
|
@ -31,7 +31,8 @@ end
|
|||
|
||||
require "./network"
|
||||
|
||||
class AuthD::Service
|
||||
# Provides a JWT-based authentication scheme for service-specific users.
|
||||
class AuthD::Service < IPC::Server
|
||||
property configuration : Baguette::Configuration::Auth
|
||||
|
||||
# DB and its indexes.
|
||||
|
@ -52,6 +53,8 @@ class AuthD::Service
|
|||
if @configuration.recreate_indexes
|
||||
@users.reindex_everything!
|
||||
end
|
||||
|
||||
super "authd"
|
||||
end
|
||||
|
||||
def hash_password(password : String) : String
|
||||
|
@ -88,6 +91,9 @@ class AuthD::Service
|
|||
|
||||
response = begin
|
||||
request.handle self, event
|
||||
rescue e : UserNotFound
|
||||
Baguette::Log.error "#{request_name} user not found"
|
||||
AuthD::Response::Error.new "authorization error"
|
||||
rescue e : AuthorizationException
|
||||
Baguette::Log.error "#{request_name} authorization error"
|
||||
AuthD::Response::Error.new "authorization error"
|
||||
|
@ -126,13 +132,12 @@ class AuthD::Service
|
|||
end
|
||||
|
||||
def run
|
||||
##
|
||||
# Provides a JWT-based authentication scheme for service-specific users.
|
||||
server = IPC::Server.new "auth"
|
||||
server.base_timer = @configuration.ipc_timer
|
||||
server.timer = @configuration.ipc_timer
|
||||
Baguette::Log.title "Starting authd"
|
||||
|
||||
server.loop do |event|
|
||||
@base_timer = @configuration.ipc_timer
|
||||
@timer = @configuration.ipc_timer
|
||||
|
||||
self.loop do |event|
|
||||
case event
|
||||
when IPC::Event::Timer
|
||||
Baguette::Log.debug "Timer" if @configuration.print_ipc_timer
|
||||
|
|
|
@ -3,7 +3,7 @@ require "json"
|
|||
require "ipc/json"
|
||||
|
||||
class IPC::JSON
|
||||
def handle(service : IPC::Server, event : IPC::Event::Events)
|
||||
def handle(service : AuthD::Service, event : IPC::Event::Events)
|
||||
raise "unimplemented"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ class AuthD::Request
|
|||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
begin
|
||||
user = authd.users_per_login.get request.login
|
||||
user = authd.users_per_login.get @login
|
||||
rescue e : DODB::MissingEntry
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
@ -17,7 +17,7 @@ class AuthD::Request
|
|||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.password_hash != authd.hash_password request.password
|
||||
if user.password_hash != authd.hash_password @password
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
|
@ -49,27 +49,27 @@ class AuthD::Request
|
|||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
# No verification of the users' informations when an admin adds it.
|
||||
# No mail address verification.
|
||||
if request.shared_key != authd.configuration.shared_key
|
||||
if @shared_key != authd.configuration.shared_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
if authd.users_per_login.get? request.login
|
||||
if authd.users_per_login.get? @login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
if authd.configuration.require_email && request.email.nil?
|
||||
if authd.configuration.require_email && @email.nil?
|
||||
return Response::Error.new "email required"
|
||||
end
|
||||
|
||||
password_hash = authd.hash_password request.password
|
||||
password_hash = authd.hash_password @password
|
||||
|
||||
uid = authd.new_uid
|
||||
|
||||
user = User.new uid, request.login, password_hash
|
||||
user.contact.email = request.email unless request.email.nil?
|
||||
user.contact.phone = request.phone unless request.phone.nil?
|
||||
user = User.new uid, @login, password_hash
|
||||
user.contact.email = @email unless @email.nil?
|
||||
user.contact.phone = @phone unless @phone.nil?
|
||||
|
||||
request.profile.try do |profile|
|
||||
@profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
|
@ -91,7 +91,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.users_per_login.get? request.login
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "user not found"
|
||||
|
@ -102,7 +102,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
# remove the user contact activation key: the email is validated
|
||||
if user.contact.activation_key == request.activation_key
|
||||
if user.contact.activation_key == @activation_key
|
||||
user.contact.activation_key = nil
|
||||
else
|
||||
return Response::Error.new "wrong activation key"
|
||||
|
@ -122,7 +122,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
uid_or_login = request.user
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
|
@ -146,13 +146,13 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.users_per_login.get? request.login
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if authd.hash_password(request.password) != user.password_hash
|
||||
if authd.hash_password(@password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
|
@ -179,11 +179,11 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
if request.shared_key != authd.configuration.shared_key
|
||||
if @shared_key != authd.configuration.shared_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
uid_or_login = request.user
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
|
@ -194,15 +194,15 @@ class AuthD::Request
|
|||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
request.password.try do |s|
|
||||
@password.try do |s|
|
||||
user.password_hash = authd.hash_password s
|
||||
end
|
||||
|
||||
request.email.try do |email|
|
||||
@email.try do |email|
|
||||
user.contact.email = email
|
||||
end
|
||||
|
||||
request.phone.try do |phone|
|
||||
@phone.try do |phone|
|
||||
user.contact.phone = phone
|
||||
end
|
||||
|
||||
|
@ -228,11 +228,11 @@ class AuthD::Request
|
|||
return Response::Error.new "registrations not allowed"
|
||||
end
|
||||
|
||||
if authd.users_per_login.get? request.login
|
||||
if authd.users_per_login.get? @login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
if authd.configuration.require_email && request.email.nil?
|
||||
if authd.configuration.require_email && @email.nil?
|
||||
return Response::Error.new "email required"
|
||||
end
|
||||
|
||||
|
@ -242,10 +242,10 @@ class AuthD::Request
|
|||
return Response::Error.new "No activation URL were entered. Cannot send activation mails."
|
||||
end
|
||||
|
||||
if ! request.email.nil?
|
||||
if ! @email.nil?
|
||||
# Test on the email address format.
|
||||
grok = Grok.new [ "%{EMAILADDRESS:email}" ]
|
||||
result = grok.parse request.email.not_nil!
|
||||
result = grok.parse @email.not_nil!
|
||||
email = result["email"]?
|
||||
|
||||
if email.nil?
|
||||
|
@ -254,18 +254,18 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
# In this case we should not accept its registration.
|
||||
if request.password.size < 4
|
||||
if @password.size < 4
|
||||
return Response::Error.new "password too short"
|
||||
end
|
||||
|
||||
uid = authd.new_uid
|
||||
password = authd.hash_password request.password
|
||||
password = authd.hash_password @password
|
||||
|
||||
user = User.new uid, request.login, password
|
||||
user.contact.email = request.email unless request.email.nil?
|
||||
user.contact.phone = request.phone unless request.phone.nil?
|
||||
user = User.new uid, @login, password
|
||||
user.contact.email = @email unless @email.nil?
|
||||
user.contact.phone = @phone unless @phone.nil?
|
||||
|
||||
request.profile.try do |profile|
|
||||
@profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
|
@ -313,17 +313,17 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.users_per_login.get? request.login
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if authd.hash_password(request.old_password) != user.password_hash
|
||||
if authd.hash_password(@old_password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
user.password_hash = authd.hash_password request.new_password
|
||||
user.password_hash = authd.hash_password @new_password
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
|
@ -341,7 +341,7 @@ class AuthD::Request
|
|||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
# FIXME: Lines too long, repeatedly (>80c with 4c tabs).
|
||||
request.token.try do |token|
|
||||
@token.try do |token|
|
||||
user = authd.get_user_from_token token
|
||||
|
||||
return Response::Error.new "unauthorized (user not found from token)"
|
||||
|
@ -349,11 +349,11 @@ class AuthD::Request
|
|||
return Response::Error.new "unauthorized (user not in authd group)" unless user.permissions["authd"]?.try(&.["*"].>=(User::PermissionLevel::Read))
|
||||
end
|
||||
|
||||
request.key.try do |key|
|
||||
@key.try do |key|
|
||||
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
|
||||
end
|
||||
|
||||
return Response::Error.new "unauthorized (no key nor token)" unless request.key || request.token
|
||||
return Response::Error.new "unauthorized (no key nor token)" unless @key || @token
|
||||
|
||||
Response::UsersList.new authd.users.to_h.map &.[1].to_public
|
||||
end
|
||||
|
@ -374,7 +374,7 @@ class AuthD::Request
|
|||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
authorized = false
|
||||
|
||||
if key = request.shared_key
|
||||
if key = @shared_key
|
||||
if key == authd.configuration.shared_key
|
||||
authorized = true
|
||||
else
|
||||
|
@ -382,14 +382,14 @@ class AuthD::Request
|
|||
end
|
||||
end
|
||||
|
||||
if token = request.token
|
||||
if token = @token
|
||||
user = authd.get_user_from_token token
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "token does not match user"
|
||||
end
|
||||
|
||||
if user.login != request.user && user.uid != request.user
|
||||
if user.login != @user && user.uid != @user
|
||||
return Response::Error.new "token does not match user"
|
||||
end
|
||||
|
||||
|
@ -400,7 +400,7 @@ class AuthD::Request
|
|||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = case u = request.user
|
||||
user = case u = @user
|
||||
when .is_a? Int32
|
||||
authd.users_per_uid.get? u.to_s
|
||||
else
|
||||
|
@ -411,20 +411,20 @@ class AuthD::Request
|
|||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = request.service
|
||||
service = @service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
resource_permissions = service_permissions[request.resource]?
|
||||
resource_permissions = service_permissions[@resource]?
|
||||
|
||||
if resource_permissions.nil?
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
return Response::PermissionCheck.new service, request.resource, user.uid, resource_permissions
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, resource_permissions
|
||||
end
|
||||
end
|
||||
AuthD.requests << CheckPermission
|
||||
|
@ -441,17 +441,17 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
unless request.shared_key == authd.configuration.shared_key
|
||||
unless @shared_key == authd.configuration.shared_key
|
||||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = authd.users_per_uid.get? request.user.to_s
|
||||
user = authd.users_per_uid.get? @user.to_s
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = request.service
|
||||
service = @service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
|
@ -459,15 +459,15 @@ class AuthD::Request
|
|||
user.permissions[service] = service_permissions
|
||||
end
|
||||
|
||||
if request.permission.none?
|
||||
service_permissions.delete request.resource
|
||||
if @permission.none?
|
||||
service_permissions.delete @resource
|
||||
else
|
||||
service_permissions[request.resource] = request.permission
|
||||
service_permissions[@resource] = @permission
|
||||
end
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::PermissionSet.new user.uid, service, request.resource, request.permission
|
||||
Response::PermissionSet.new user.uid, service, @resource, @permission
|
||||
end
|
||||
end
|
||||
AuthD.requests << SetPermission
|
||||
|
@ -481,7 +481,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
uid_or_login = request.user
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
|
@ -492,8 +492,8 @@ class AuthD::Request
|
|||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
if user.password_renew_key == request.password_renew_key
|
||||
user.password_hash = authd.hash_password request.new_password
|
||||
if user.password_renew_key == @password_renew_key
|
||||
user.password_hash = authd.hash_password @new_password
|
||||
else
|
||||
return Response::Error.new "renew key not valid"
|
||||
end
|
||||
|
@ -515,7 +515,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
uid_or_login = request.user
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
|
@ -526,7 +526,7 @@ class AuthD::Request
|
|||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
if user.contact.email != request.email
|
||||
if user.contact.email != @email
|
||||
# Same error as when users are not found.
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
@ -575,7 +575,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
pattern = Regex.new request.user, Regex::Options::IGNORE_CASE
|
||||
pattern = Regex.new @user, Regex::Options::IGNORE_CASE
|
||||
|
||||
matching_users = Array(AuthD::User::Public).new
|
||||
|
||||
|
@ -609,11 +609,11 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.get_user_from_token request.token
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
new_profile = request.new_profile
|
||||
new_profile = @new_profile
|
||||
|
||||
profile = user.profile || Hash(String, JSON::Any).new
|
||||
|
||||
|
@ -648,43 +648,39 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = if token = request.token
|
||||
user = authd.get_user_from_token token
|
||||
user = if token = @token
|
||||
u = authd.get_user_from_token token
|
||||
raise UserNotFound.new unless u
|
||||
u
|
||||
elsif shared_key = @shared_key
|
||||
raise AdminAuthorizationException.new if shared_key != authd.configuration.shared_key
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
u = @user
|
||||
raise UserNotFound.new unless u
|
||||
|
||||
user
|
||||
elsif shared_key = request.shared_key
|
||||
return Response::Error.new "invalid shared key" if shared_key != authd.configuration.shared_key
|
||||
|
||||
user = request.user
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
user = if user.is_a? Int32
|
||||
authd.users_per_uid.get? user.to_s
|
||||
u = if u.is_a? Int32
|
||||
authd.users_per_uid.get? u.to_s
|
||||
else
|
||||
authd.users_per_login.get? user
|
||||
authd.users_per_login.get? u
|
||||
end
|
||||
raise UserNotFound.new unless u
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
user
|
||||
u
|
||||
else
|
||||
return Response::Error.new "no token or shared_key/user pair"
|
||||
raise AuthenticationInfoLacking.new
|
||||
end
|
||||
|
||||
new_profile = user.profile || Hash(String, JSON::Any).new
|
||||
|
||||
unless request.shared_key
|
||||
unless @shared_key
|
||||
authd.configuration.read_only_profile_keys.each do |key|
|
||||
if request.new_profile.has_key? key
|
||||
if @new_profile.has_key? key
|
||||
return Response::Error.new "tried to edit read only key"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
request.new_profile.each do |key, value|
|
||||
@new_profile.each do |key, value|
|
||||
new_profile[key] = value
|
||||
end
|
||||
|
||||
|
@ -707,11 +703,11 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.get_user_from_token request.token
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
if email = request.email
|
||||
if email = @email
|
||||
# FIXME: This *should* require checking the new mail, with
|
||||
# a new activation key and everything else.
|
||||
user.contact.email = email
|
||||
|
@ -739,7 +735,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
uid_or_login = request.user
|
||||
uid_or_login = @user
|
||||
user_to_delete = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
|
@ -752,11 +748,11 @@ class AuthD::Request
|
|||
|
||||
# Either the request comes from an admin or the user.
|
||||
# Shared key == admin, check the key.
|
||||
if key = request.shared_key
|
||||
if key = @shared_key
|
||||
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
|
||||
else
|
||||
login = request.login
|
||||
pass = request.password
|
||||
login = @login
|
||||
pass = @password
|
||||
if login.nil? || pass.nil?
|
||||
return Response::Error.new "authentication failed (no shared key, no login)"
|
||||
end
|
||||
|
@ -798,7 +794,7 @@ class AuthD::Request
|
|||
end
|
||||
|
||||
def handle(authd : AuthD::Service, event : IPC::Event::Events)
|
||||
user = authd.get_user_from_token request.token
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
|
|
Loading…
Reference in New Issue