mentorenwahl/backend/src/backend/api/context.cr

210 lines
5.8 KiB
Crystal
Raw Normal View History

2022-02-10 07:43:47 +00:00
# Mentorenwahl: A fullstack application for assigning mentors to students based on their whishes.
# Copyright (C) 2022 Dominic Grimm
2022-03-07 13:06:02 +00:00
#
2022-02-10 07:43:47 +00:00
# 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.
2022-03-07 13:06:02 +00:00
#
2022-02-10 07:43:47 +00:00
# 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.
2022-03-07 13:06:02 +00:00
#
2022-02-10 07:43:47 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2022-01-23 08:12:57 +00:00
require "graphql"
require "http/headers"
2022-10-31 08:47:26 +00:00
require "jwt/errors"
require "json"
2022-01-23 08:12:57 +00:00
module Backend
module Api
2022-02-09 13:35:35 +00:00
# GraphQL request context class
2022-01-23 08:12:57 +00:00
class Context < GraphQL::Context
# Development mode
2022-07-28 12:05:10 +00:00
getter development
2022-10-31 08:47:26 +00:00
# Request status
getter status
2022-02-09 13:35:35 +00:00
# Authenticated user
2022-07-28 12:05:10 +00:00
getter user
2022-02-09 13:35:35 +00:00
# User is admin
2022-07-28 12:05:10 +00:00
getter admin
2022-02-09 13:35:35 +00:00
# User's role
2022-07-28 12:05:10 +00:00
getter role
2022-02-09 13:35:35 +00:00
# User's external object
2022-07-28 12:05:10 +00:00
getter external
def initialize(
@development : Bool,
2022-10-31 08:47:26 +00:00
@status : Status,
2022-07-28 12:05:10 +00:00
@user : Db::User?,
@admin : Bool?,
@role : Schema::UserRole?,
@external : (Db::Teacher | Db::Student)?
)
end
2022-01-23 08:12:57 +00:00
2022-10-31 08:47:26 +00:00
def initialize(headers : HTTP::Headers, @development : Bool, @status = Status::OK, *rest)
super(*rest)
2022-01-23 08:12:57 +00:00
2022-10-31 08:47:26 +00:00
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
2022-11-04 20:23:36 +00:00
pp! payload
2022-11-12 21:50:06 +00:00
if payload.iss != "Mentorenwahl" || payload.vrs != Backend::VERSION
@status = Status::JWTError
elsif @user = Db::User.find(payload.context.user)
2022-10-31 08:47:26 +00:00
@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
2022-01-23 08:12:57 +00:00
end
end
end
2022-10-31 08:47:26 +00:00
def on_development : Nil
{% if !flag?(:release) %}
if @development
yield
end
{% end %}
end
enum Status
OK
SessionExpired
JWTError
end
2022-02-09 13:35:35 +00:00
# User is authenticated
2022-01-23 08:12:57 +00:00
def authenticated? : Bool
2022-10-31 08:47:26 +00:00
!!@user && @status.ok?
2022-01-23 08:12:57 +00:00
end
2022-02-09 13:35:35 +00:00
# :ditto:
2022-01-23 08:12:57 +00:00
def authenticated! : Bool
2022-11-04 20:23:36 +00:00
# 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?
2022-01-23 08:12:57 +00:00
true
end
2022-02-09 13:35:35 +00:00
# User is admin
2022-02-06 15:42:08 +00:00
def admin? : Bool
authenticated? && !!@admin
end
2022-02-09 13:35:35 +00:00
# :ditto:
2022-02-06 15:42:08 +00:00
def admin! : Bool
2022-10-31 08:47:26 +00:00
authenticated!
2022-11-04 20:23:36 +00:00
# raise "Invalid permissions" unless admin?
raise Errors::InvalidPermissions.new unless admin?
2022-02-06 15:42:08 +00:00
true
end
2022-02-09 13:35:35 +00:00
# User's is one of *roles*
2022-10-31 08:47:26 +00:00
def role?(roles : Array(Schema::UserRole), external_check = true) : Bool
2022-01-23 08:12:57 +00:00
return false unless authenticated?
roles.each do |role|
return true if @role == role &&
if external_check
role ==
2022-10-31 08:47:26 +00:00
case @external.not_nil!
when Db::Teacher
Schema::UserRole::Teacher
when Db::Student
Schema::UserRole::Student
end
2022-01-23 08:12:57 +00:00
else
true
end
end
false
end
2022-02-09 13:35:35 +00:00
# :ditto:
2022-10-31 08:47:26 +00:00
def role!(roles : Array(Schema::UserRole), external_check = true) : Bool
authenticated!
2022-11-04 20:23:36 +00:00
# raise "Invalid permissions" unless role?(roles, external_check)
raise Errors::InvalidPermissions.new unless role?(roles, external_check)
2022-01-23 08:12:57 +00:00
true
end
2022-02-09 13:35:35 +00:00
# User is teacher
2022-02-13 12:08:04 +00:00
def teacher?(external_check = true) : Bool
2022-10-31 08:47:26 +00:00
role?([Schema::UserRole::Teacher], external_check)
2022-02-06 15:42:08 +00:00
end
2022-01-23 08:12:57 +00:00
2022-02-09 13:35:35 +00:00
# :ditto:
2022-02-13 12:08:04 +00:00
def teacher!(external_check = true) : Bool
2022-10-31 08:47:26 +00:00
role!([Schema::UserRole::Teacher], external_check)
2022-02-06 15:42:08 +00:00
end
2022-01-23 08:12:57 +00:00
2022-02-09 13:35:35 +00:00
# User is student
2022-02-13 12:08:04 +00:00
def student?(external_check = true) : Bool
2022-10-31 08:47:26 +00:00
role?([Schema::UserRole::Student], external_check)
2022-02-06 15:42:08 +00:00
end
2022-01-23 08:12:57 +00:00
2022-02-09 13:35:35 +00:00
# :ditto:
2022-02-13 12:08:04 +00:00
def student!(external_check = true) : Bool
2022-10-31 08:47:26 +00:00
role!([Schema::UserRole::Student], external_check)
2022-01-23 08:12:57 +00:00
end
2022-10-31 08:47:26 +00:00
# Custom error handler
def handle_exception(ex : Exception) : String?
2022-11-04 20:23:36 +00:00
# pp! ex, ex.message, ex.class, typeof(ex), ex.is_a? Errors::PublicError
# # ex.message
2022-10-31 08:47:26 +00:00
case ex
2022-11-04 20:23:36 +00:00
when Errors::PublicError
ex.api_message
when Errors::PrivateError
2022-10-31 08:47:26 +00:00
{% if !flag?(:release) %}
if @development
2022-11-04 20:23:36 +00:00
ex.api_message
2022-10-31 08:47:26 +00:00
else
2022-11-04 20:23:36 +00:00
Errors::UNKNOWN_PRIVATE_ERROR
2022-10-31 08:47:26 +00:00
end
{% else %}
2022-11-04 20:23:36 +00:00
Errors::UNKNOWN_PRIVATE_ERROR
2022-10-31 08:47:26 +00:00
{% end %}
else
{% if !flag?(:release) %}
if @development
2022-11-04 20:23:36 +00:00
ex.message || Errors::UNKNOWN_PRIVATE_ERROR
2022-10-31 08:47:26 +00:00
else
2022-11-04 20:23:36 +00:00
Errors::UNKNOWN_PRIVATE_ERROR
2022-10-31 08:47:26 +00:00
end
{% else %}
2022-11-04 20:23:36 +00:00
Errors::UNKNOWN_PRIVATE_ERROR
2022-10-31 08:47:26 +00:00
{% end %}
end
end
2022-01-23 08:12:57 +00:00
end
end
end