203 lines
5.6 KiB
Crystal
203 lines
5.6 KiB
Crystal
# Mentorenwahl: A fullstack application for assigning mentors to students based on their whishes.
|
|
# Copyright (C) 2022 Dominic Grimm
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
require "graphql"
|
|
require "http/headers"
|
|
require "jwt/errors"
|
|
require "json"
|
|
require "uuid"
|
|
|
|
module Backend
|
|
module Api
|
|
# GraphQL request context class
|
|
class Context < GraphQL::Context
|
|
# Request status
|
|
getter status
|
|
|
|
# Authenticated user
|
|
getter user
|
|
|
|
# User is admin
|
|
getter admin
|
|
|
|
# User's role
|
|
getter role
|
|
|
|
# User's external object
|
|
getter external
|
|
|
|
# JTI of request token
|
|
getter jti
|
|
|
|
def initialize(
|
|
@status : Status,
|
|
@user : Db::User?,
|
|
@admin : Bool?,
|
|
@role : Schema::UserRole?,
|
|
@external : (Db::Teacher | Db::Student)?,
|
|
@jti : UUID?
|
|
)
|
|
end
|
|
|
|
def initialize(headers : HTTP::Headers, @status = Status::OK, *rest)
|
|
super(*rest)
|
|
|
|
if (token = headers["authorization"]?) && token.starts_with?(Auth::BEARER)
|
|
begin
|
|
payload = Auth::Token.decode(token[Auth::BEARER.size..].strip)
|
|
rescue ex : JWT::ExpiredSignatureError
|
|
@status = Status::SessionExpired
|
|
rescue
|
|
@status = Status::JWTError
|
|
else
|
|
if @user = Db::User.find(payload.user_id)
|
|
@jti = payload.jti
|
|
if Db::Token.query.where { (id == payload.jti) & active }.first.nil?
|
|
@status = Status::SessionExpired
|
|
else
|
|
@admin = user.not_nil!.admin
|
|
@role = user.not_nil!.role.to_api
|
|
@external =
|
|
case @role.not_nil!
|
|
when .teacher?
|
|
@user.not_nil!.teacher
|
|
when .student?
|
|
@user.not_nil!.student
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def on_development : Nil
|
|
{% if !flag?(:release) %}
|
|
yield
|
|
{% end %}
|
|
end
|
|
|
|
enum Status
|
|
OK
|
|
SessionExpired
|
|
JWTError
|
|
end
|
|
|
|
# User is authenticated
|
|
def authenticated? : Bool
|
|
!!@user && @status.ok?
|
|
end
|
|
|
|
# :ditto:
|
|
def authenticated! : Bool
|
|
# raise "Session expired" if @status.session_expired?
|
|
raise Errors::SessionExpired.new if @status.session_expired?
|
|
# raise "Not authenticated" unless authenticated?
|
|
raise Errors::NotAuthenticated.new unless authenticated?
|
|
|
|
true
|
|
end
|
|
|
|
# User is admin
|
|
def admin? : Bool
|
|
authenticated? && !!@admin
|
|
end
|
|
|
|
# :ditto:
|
|
def admin! : Bool
|
|
authenticated!
|
|
# raise "Invalid permissions" unless admin?
|
|
raise Errors::InvalidPermissions.new unless admin?
|
|
|
|
true
|
|
end
|
|
|
|
# User's is one of *roles*
|
|
def role?(roles : Array(Schema::UserRole), external_check = true) : Bool
|
|
return false unless authenticated?
|
|
|
|
roles.each do |role|
|
|
return true if @role == role &&
|
|
if external_check
|
|
role ==
|
|
case @external.not_nil!
|
|
when Db::Teacher
|
|
Schema::UserRole::Teacher
|
|
when Db::Student
|
|
Schema::UserRole::Student
|
|
end
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
false
|
|
end
|
|
|
|
# :ditto:
|
|
def role!(roles : Array(Schema::UserRole), external_check = true) : Bool
|
|
authenticated!
|
|
# raise "Invalid permissions" unless role?(roles, external_check)
|
|
raise Errors::InvalidPermissions.new unless role?(roles, external_check)
|
|
|
|
true
|
|
end
|
|
|
|
# User is teacher
|
|
def teacher?(external_check = true) : Bool
|
|
role?([Schema::UserRole::Teacher], external_check)
|
|
end
|
|
|
|
# :ditto:
|
|
def teacher!(external_check = true) : Bool
|
|
role!([Schema::UserRole::Teacher], external_check)
|
|
end
|
|
|
|
# User is student
|
|
def student?(external_check = true) : Bool
|
|
role?([Schema::UserRole::Student], external_check)
|
|
end
|
|
|
|
# :ditto:
|
|
def student!(external_check = true) : Bool
|
|
role!([Schema::UserRole::Student], external_check)
|
|
end
|
|
|
|
# Custom error handler
|
|
def handle_exception(ex : Exception) : String?
|
|
# pp! ex, ex.message, ex.class, typeof(ex), ex.is_a? Errors::PublicError
|
|
# # ex.message
|
|
|
|
case ex
|
|
when Errors::PublicError
|
|
ex.api_message
|
|
when Errors::PrivateError
|
|
{% if !flag?(:release) %}
|
|
ex.api_message
|
|
{% else %}
|
|
Errors::UNKNOWN_PRIVATE_ERROR
|
|
{% end %}
|
|
else
|
|
{% if !flag?(:release) %}
|
|
ex.message || Errors::UNKNOWN_PRIVATE_ERROR
|
|
{% else %}
|
|
Errors::UNKNOWN_PRIVATE_ERROR
|
|
{% end %}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|