commit
2d06060105
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
# authd
|
||||||
|
|
||||||
|
## Database setup
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create table users(id int, created_at date, updated_at date, username text, realname text, password text, avatar text, perms text[]);
|
||||||
|
```
|
||||||
|
|
|
@ -11,6 +11,8 @@ description: |
|
||||||
targets:
|
targets:
|
||||||
authd:
|
authd:
|
||||||
main: src/main.cr
|
main: src/main.cr
|
||||||
|
authd-adduser:
|
||||||
|
main: src/adduser.cr
|
||||||
|
|
||||||
crystal: 0.26
|
crystal: 0.26
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
require "kemal"
|
||||||
|
require "jwt"
|
||||||
|
|
||||||
|
require "./user.cr"
|
||||||
|
|
||||||
|
class HTTP::Server::Context
|
||||||
|
property authd_user : AuthD::User?
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthD::Middleware < Kemal::Handler
|
||||||
|
property key : String = ""
|
||||||
|
|
||||||
|
@configured = false
|
||||||
|
@configurator : Proc(Middleware, Nil)
|
||||||
|
|
||||||
|
def initialize(&block : Proc(Middleware, Nil))
|
||||||
|
@configurator = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(context)
|
||||||
|
unless @configured
|
||||||
|
@configured = true
|
||||||
|
@configurator.call self
|
||||||
|
end
|
||||||
|
|
||||||
|
context.request.headers["X-Token"]?.try do |x_token|
|
||||||
|
payload, header = JWT.decode x_token, @key, "HS256"
|
||||||
|
|
||||||
|
if payload
|
||||||
|
context.authd_user = AuthD::User.new.tap do |u|
|
||||||
|
u.username = payload["username"].as_s?
|
||||||
|
u.realname = payload["realname"].as_s?
|
||||||
|
u.avatar = payload["avatar"].as_s?
|
||||||
|
u.perms = Array(String).new
|
||||||
|
|
||||||
|
payload["perms"].as_a.tap do |perms|
|
||||||
|
perms.each do |perm|
|
||||||
|
if perm.class == String
|
||||||
|
u.perms! << perm.as_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
call_next context
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
62
src/main.cr
62
src/main.cr
|
@ -6,48 +6,33 @@ require "jwt"
|
||||||
require "pg"
|
require "pg"
|
||||||
require "crecto"
|
require "crecto"
|
||||||
|
|
||||||
MASTER_KEY = Random::Secure.base64
|
require "./user.cr"
|
||||||
authd_db_password_file = "db-password-file"
|
|
||||||
authd_db_name = "authd"
|
authd_db_name = "authd"
|
||||||
authd_db_hostname = "localhost"
|
authd_db_hostname = "localhost"
|
||||||
authd_db_user = "user"
|
authd_db_user = "user"
|
||||||
|
authd_db_password = "nico-nico-nii"
|
||||||
|
authd_jwt_key = "nico-nico-nii"
|
||||||
|
|
||||||
Kemal.config.extra_options do |parser|
|
Kemal.config.extra_options do |parser|
|
||||||
parser.on "-d name", "--database-name name", "database name for authd" do |dbn|
|
parser.on "-d name", "--database-name name", "Database name." do |name|
|
||||||
authd_db_name = dbn
|
authd_db_name = name
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.on "-u name", "--database-username user", "database user for authd" do |u|
|
parser.on "-u name", "--database-username user", "Database user." do |name|
|
||||||
authd_db_user = u
|
authd_db_user = name
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.on "-a hostname", "--hostname host", "hostname for authd" do |h|
|
parser.on "-a host", "--hostname host", "Database host name." do |host|
|
||||||
authd_db_hostname = h
|
authd_db_hostname = host
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.on "-P password-file", "--passfile file", "password file for authd" do |f|
|
parser.on "-P file", "--password-file file", "Password file." do |file_name|
|
||||||
authd_db_password_file = f
|
authd_db_password = File.read(file_name).chomp
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class User < Crecto::Model
|
|
||||||
schema "users" do # table name
|
|
||||||
field :username, String
|
|
||||||
field :realname, String
|
|
||||||
field :avatar, String
|
|
||||||
field :password, String
|
|
||||||
field :perms, Array(String)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
validate_required [:username, :password, :perms]
|
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
|
||||||
|
authd_jwt_key = File.read(file_name).chomp
|
||||||
def to_h
|
|
||||||
{
|
|
||||||
:username => @username,
|
|
||||||
:realname => @realname,
|
|
||||||
:perms => @perms,
|
|
||||||
:avatar => @avatar
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,7 +50,7 @@ post "/token" do |env|
|
||||||
next halt env, status_code: 400, response: ({error: "Missing password."}.to_json)
|
next halt env, status_code: 400, response: ({error: "Missing password."}.to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
user = MyRepo.get_by(User, username: username, password: password)
|
user = DataBase.get_by AuthD::User, username: username, password: password
|
||||||
|
|
||||||
if ! user
|
if ! user
|
||||||
next halt env, status_code: 400, response: ({error: "Invalid user or password."}.to_json)
|
next halt env, status_code: 400, response: ({error: "Invalid user or password."}.to_json)
|
||||||
|
@ -73,20 +58,29 @@ post "/token" do |env|
|
||||||
|
|
||||||
{
|
{
|
||||||
"status" => "success",
|
"status" => "success",
|
||||||
"token" => JWT.encode(user.to_h, MASTER_KEY, "HS256")
|
"token" => JWT.encode user.to_h, authd_jwt_key, "HS256"
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
module MyRepo
|
module DataBase
|
||||||
extend Crecto::Repo
|
extend Crecto::Repo
|
||||||
end
|
end
|
||||||
|
|
||||||
Kemal.run do
|
Kemal.run do
|
||||||
MyRepo.config do |conf|
|
DataBase.config do |conf|
|
||||||
conf.adapter = Crecto::Adapters::Postgres
|
conf.adapter = Crecto::Adapters::Postgres
|
||||||
conf.hostname = authd_db_hostname
|
conf.hostname = authd_db_hostname
|
||||||
conf.database = authd_db_name
|
conf.database = authd_db_name
|
||||||
conf.username = authd_db_user
|
conf.username = authd_db_user
|
||||||
conf.password = File.read authd_db_password_file
|
conf.password = authd_db_password
|
||||||
|
end
|
||||||
|
|
||||||
|
# Dummy query to check DB connection is possible.
|
||||||
|
begin
|
||||||
|
DataBase.all AuthD::User, Crecto::Repo::Query.new
|
||||||
|
rescue e
|
||||||
|
puts "Database connection failed: #{e.message}"
|
||||||
|
|
||||||
|
Kemal.stop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
require "pg"
|
||||||
|
require "crecto"
|
||||||
|
|
||||||
|
class AuthD::User < Crecto::Model
|
||||||
|
schema "users" do # table name
|
||||||
|
field :username, String
|
||||||
|
field :realname, String
|
||||||
|
field :avatar, String
|
||||||
|
field :password, String
|
||||||
|
field :perms, Array(String)
|
||||||
|
end
|
||||||
|
|
||||||
|
validate_required [:username, :password, :perms]
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{
|
||||||
|
:username => @username,
|
||||||
|
:realname => @realname,
|
||||||
|
:perms => @perms,
|
||||||
|
:avatar => @avatar
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue