gaku_api 0.3.0.pre.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +17 -0
  3. data/app/controllers/gaku/api/application_controller.rb +3 -0
  4. data/app/controllers/gaku/api/v1/authentication_controller.rb +34 -0
  5. data/app/controllers/gaku/api/v1/base_controller.rb +53 -0
  6. data/app/controllers/gaku/api/v1/class_groups/enrollments_controller.rb +44 -0
  7. data/app/controllers/gaku/api/v1/class_groups/students_controller.rb +22 -0
  8. data/app/controllers/gaku/api/v1/courses/enrollments_controller.rb +44 -0
  9. data/app/controllers/gaku/api/v1/courses/students_controller.rb +22 -0
  10. data/app/controllers/gaku/api/v1/courses_controller.rb +54 -0
  11. data/app/controllers/gaku/api/v1/enrollments_controller.rb +22 -0
  12. data/app/controllers/gaku/api/v1/statuses_controller.rb +24 -0
  13. data/app/controllers/gaku/api/v1/students/class_groups_controller.rb +27 -0
  14. data/app/controllers/gaku/api/v1/students/courses_controller.rb +27 -0
  15. data/app/controllers/gaku/api/v1/students/exam_sessions_controller.rb +27 -0
  16. data/app/controllers/gaku/api/v1/students/extracurricular_activities_controller.rb +27 -0
  17. data/app/controllers/gaku/api/v1/students/guardians_controller.rb +27 -0
  18. data/app/controllers/gaku/api/v1/students/student_guardians_controller.rb +29 -0
  19. data/app/controllers/gaku/api/v1/students_controller.rb +67 -0
  20. data/app/controllers/gaku/api/v1/syllabuses_controller.rb +54 -0
  21. data/app/serializers/gaku/class_group_serializer.rb +6 -0
  22. data/app/serializers/gaku/course_serializer.rb +7 -0
  23. data/app/serializers/gaku/enrollment_serializer.rb +14 -0
  24. data/app/serializers/gaku/exam_session_serializer.rb +5 -0
  25. data/app/serializers/gaku/extracurricular_activity_serializer.rb +5 -0
  26. data/app/serializers/gaku/guardian_serializer.rb +9 -0
  27. data/app/serializers/gaku/student_serializer.rb +12 -0
  28. data/app/serializers/gaku/syllabus_serializer.rb +5 -0
  29. data/app/services/gaku/api/authenticate_user.rb +42 -0
  30. data/app/services/gaku/api/authorize_api_request.rb +39 -0
  31. data/app/services/gaku/api/refresh_authenticate_user.rb +50 -0
  32. data/config/initializers/kaminari.rb +3 -0
  33. data/config/initializers/mime_types.rb +1 -0
  34. data/config/routes.rb +31 -0
  35. data/gaku_api.gemspec +25 -0
  36. data/lib/gaku/api/engine.rb +28 -0
  37. data/lib/gaku/api/version.rb +5 -0
  38. data/lib/gaku/api.rb +15 -0
  39. data/lib/gaku_api.rb +1 -0
  40. data/lib/json_web_token.rb +24 -0
  41. data/lib/tasks/api_tasks.rake +4 -0
  42. metadata +184 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2022049a291ae30871d7cb99cd7e634c4793337a99da7b223b88247cc12da3a4
4
+ data.tar.gz: 539d9b8910f2ae44c1c315bb77121170bdb5940f664d3a7a0a60c5fc2ac8ab80
5
+ SHA512:
6
+ metadata.gz: d705d970e7707a30c5ac0ed92f9bcec973fff85c7a5bc01fa1cbb2efa2d6e105db378f28266643ddb4936bf64aaadfe7facf8a80159effab92b182b5bca0ddba
7
+ data.tar.gz: b8487fb3e71bc6467425a4fc9e32230cf1ba182bbfeca0a5450fbc034c05aaa61f43e289af118ab5e15c3fe31dddba1ca90c0bab170a4ff0b6ab241afa32c483
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rubygems/package_task'
7
+ require 'gaku/testing/common_rake'
8
+
9
+ Bundler::GemHelper.install_tasks
10
+
11
+ task default: :spec
12
+
13
+ desc 'Generates a dummy app for testing'
14
+ task :test_app do
15
+ ENV['LIB_NAME'] = 'gaku/api'
16
+ Rake::Task['common:test_app'].invoke
17
+ end
@@ -0,0 +1,3 @@
1
+ class Gaku::Api::ApplicationController < ActionController::API
2
+
3
+ end
@@ -0,0 +1,34 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class AuthenticationController < BaseController
5
+ skip_before_action :authenticate_request
6
+
7
+ def authenticate
8
+ command = Gaku::Api::AuthenticateUser.call(
9
+ username: params[:username],
10
+ password: params[:password]
11
+ )
12
+
13
+ if command.success?
14
+ render(respond_format => { tokens: command.result })
15
+ else
16
+ render(respond_format => { error: command.errors }, status: :unauthorized)
17
+ end
18
+ end
19
+
20
+ def refresh
21
+ refresh_token = params[:refresh_token]
22
+ command = Gaku::Api::RefreshAuthenticateUser.call(params[:refresh_token])
23
+
24
+ if command.success?
25
+ render respond_format => { tokens: command.result }
26
+ else
27
+ render respond_format => { error: command.errors }, status: :unauthorized
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ class Gaku::Api::V1::BaseController < Gaku::Api::ApplicationController
2
+ include ActionController::MimeResponds
3
+ attr_reader :current_user
4
+
5
+ before_action :set_default_format
6
+ before_action :authenticate_request
7
+
8
+ # rescue_from StandardError do |exception|
9
+ # render respond_format => { error: exception.message }, status: 500
10
+ # end
11
+
12
+ # rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
13
+ # rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
14
+
15
+
16
+ private
17
+
18
+ def respond_format
19
+ request.format.to_sym
20
+ end
21
+
22
+ def member_respond_to(object, options = {})
23
+ render({respond_format => object, root: false, adapter: :attributes }.merge!(options))
24
+ end
25
+
26
+ def collection_respond_to(collection, options = {})
27
+ render({respond_format => collection, root: false, adapter: :json, meta: meta_for(collection)}.merge!(options))
28
+ end
29
+
30
+ def meta_for(collection)
31
+ { count: collection.size , total_count: collection.total_count, page: collection.current_page }
32
+ end
33
+
34
+ def authenticate_request
35
+ @current_user = Gaku::Api::AuthorizeApiRequest.call(request.headers).result
36
+ render respond_format => { error: 'Not Authorized' }, status: 401 unless @current_user
37
+ end
38
+
39
+ def set_default_format
40
+ unless [:json, :msgpack].include? request.format.symbol
41
+ request.format = :json
42
+ end
43
+ end
44
+
45
+ def render_unprocessable_entity_response(exception)
46
+ render(respond_format => exception.record.errors, status: :unprocessable_entity)
47
+ end
48
+
49
+ def render_not_found_response(exception)
50
+ render(respond_format => { error: "record not found" }, status: :not_found)
51
+ end
52
+
53
+ end
@@ -0,0 +1,44 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module ClassGroups
5
+ class EnrollmentsController < BaseController
6
+ before_action :set_class_group
7
+
8
+ def index
9
+ @enrollments = @class_group.enrollments.page(params[:page ])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+
12
+ end
13
+
14
+ def create
15
+ @enrollment = @class_group.enrollments.create(create_enrollment_params)
16
+ member_respond_to @enrollment
17
+ end
18
+
19
+ def update
20
+ @enrollment = Enrollment.find(params[:id])
21
+ @enrollment.update!(update_enrollment_params)
22
+ member_respond_to @enrollment
23
+ end
24
+
25
+ private
26
+
27
+ def create_enrollment_params
28
+ params.require(:student_id)
29
+ params.permit(:student_id, :seat_number)
30
+ end
31
+
32
+ def update_enrollment_params
33
+ params.permit(:seat_number)
34
+ end
35
+
36
+ def set_class_group
37
+ @class_group = ClassGroup.find(params[:class_group_id])
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module ClassGroups
5
+ class StudentsController < BaseController
6
+ before_action :set_class_group
7
+
8
+ def index
9
+ @students = @class_group.students.page(params[:page ])
10
+ collection_respond_to @students, root: :students
11
+ end
12
+
13
+ private
14
+
15
+ def set_class_group
16
+ @class_group = ClassGroup.find(params[:class_group_id])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Courses
5
+ class EnrollmentsController < BaseController
6
+ before_action :set_course
7
+
8
+ def index
9
+ @enrollments = @course.enrollments.page(params[:page ])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+
12
+ end
13
+
14
+ def create
15
+ @enrollment = @course.enrollments.create(create_enrollment_params)
16
+ member_respond_to @enrollment
17
+ end
18
+
19
+ def update
20
+ @enrollment = Enrollment.find(params[:id])
21
+ @enrollment.update!(update_enrollment_params)
22
+ member_respond_to @enrollment
23
+ end
24
+
25
+ private
26
+
27
+ def create_enrollment_params
28
+ params.require(:student_id)
29
+ params.permit(:student_id, :seat_number)
30
+ end
31
+
32
+ def update_enrollment_params
33
+ params.permit(:seat_number)
34
+ end
35
+
36
+ def set_course
37
+ @course = Course.find(params[:course_id])
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Courses
5
+ class StudentsController < BaseController
6
+ before_action :set_course
7
+
8
+ def index
9
+ @students = @course.students.page(params[:page ])
10
+ collection_respond_to @students, root: :students
11
+ end
12
+
13
+ private
14
+
15
+ def set_course
16
+ @course = Course.find(params[:course_id])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class CoursesController < BaseController
5
+
6
+ before_action :set_course, only: %i( show update destroy )
7
+
8
+ def index
9
+ @courses = Course.all.page(params[:page ])
10
+ collection_respond_to @courses, root: :courses
11
+ end
12
+
13
+ def show
14
+ member_respond_to @course
15
+ end
16
+
17
+ def create
18
+ @course = Course.new(course_params)
19
+ if @course.save!
20
+ member_respond_to @course
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @course.update!(course_params)
26
+ member_respond_to @course
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ if @course.destroy!
32
+ member_respond_to @course
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def set_course
39
+ @course = Course.find(params[:id])
40
+ end
41
+
42
+ def course_params
43
+ params.require(:code)
44
+ params.permit(course_attrs)
45
+ end
46
+
47
+ def course_attrs
48
+ %i( syllabus_id code )
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class EnrollmentsController < BaseController
5
+ skip_before_action :authenticate_request
6
+ before_action :set_enrollable
7
+
8
+ def index
9
+ @enrollments = @enrollable.enrollments.page(params[:page])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+ end
12
+
13
+ private
14
+
15
+ def set_enrollable
16
+ resource, id = request.path.split('/').values_at(3,4)
17
+ @enrollable = "gaku/#{resource}".pluralize.classify.constantize.find(id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class StatusesController < ActionController::API
5
+
6
+ def show
7
+ if msgpack_request?
8
+ render msgpack: { status: :running }
9
+
10
+ else
11
+ render json: { status: :running }
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def msgpack_request?
18
+ request.format.to_sym == :msgpack
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ClassGroupsController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @class_groups = @student.class_groups
11
+ respond_to do |format|
12
+ format.json { render json: @class_groups, root: :class_groups, adapter: :json }
13
+ format.msgpack { render msgpack: @class_groups, root: :class_groups, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class CoursesController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @courses = @student.courses
11
+ respond_to do |format|
12
+ format.json { render json: @courses, root: :courses, adapter: :json }
13
+ format.msgpack { render msgpack: @courses, root: :courses, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ExamSessionsController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @exam_sessions = @student.exam_sessions
11
+ respond_to do |format|
12
+ format.json { render json: @exam_sessions, root: :exam_sessions, adapter: :json }
13
+ format.msgpack { render msgpack: @exam_sessions, root: :exam_sessions, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ExtracurricularActivitiesController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @extracurricular_activities = @student.extracurricular_activities
11
+ respond_to do |format|
12
+ format.json { render json: @extracurricular_activities, root: :extracurricular_activities, adapter: :json }
13
+ format.msgpack { render msgpack: @extracurricular_activities, root: :extracurricular_activities, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class GuardiansController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @guardians = @student.guardians
11
+ respond_to do |format|
12
+ format.json { render json: @guardians, root: :guardians, adapter: :json }
13
+ format.msgpack { render msgpack: @guardians, root: :guardians, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class StudentGuardiansController < BaseController
6
+
7
+ before_action :set_guardian
8
+ before_action :set_student
9
+
10
+ def create
11
+ @student.guardians << @guardian
12
+ member_respond_to @guardian
13
+ end
14
+
15
+ private
16
+
17
+ def set_guardian
18
+ @guardian = Guardian.find(params[:guardian_id])
19
+ end
20
+
21
+ def set_student
22
+ @student = Student.find(params[:student_id])
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,67 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class StudentsController < BaseController
5
+
6
+ skip_before_action :authenticate_request
7
+ before_action :set_student, except: %i( index create )
8
+
9
+ def index
10
+ @q = Student.includes(:primary_contact, :primary_address).search(params[:q])
11
+ @students = @q.result.page(params[:page])
12
+ collection_respond_to @students, root: :students
13
+ end
14
+
15
+ def show
16
+ member_respond_to @student
17
+ end
18
+
19
+ def create
20
+ @student = Student.new(create_student_params)
21
+ if @student.save!
22
+ member_respond_to @student
23
+ end
24
+ end
25
+
26
+ def update
27
+ if @student.update!(update_student_params)
28
+ member_respond_to @student
29
+ end
30
+ end
31
+
32
+ def destroy
33
+ if @student.destroy!
34
+ member_respond_to @student
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def set_student
41
+ @student = Student.find(params[:id])
42
+ end
43
+
44
+ def create_student_params
45
+ params.require(required_student_attributes)
46
+ params.permit(student_attributes)
47
+ end
48
+
49
+ def update_student_params
50
+ params.permit(student_attributes)
51
+ end
52
+
53
+ def required_student_attributes
54
+ %i( name surname )
55
+ end
56
+
57
+ def student_attributes
58
+ %i(
59
+ name surname name_reading surname_reading birth_date gender scholarship_status_id,
60
+ enrollment_status_code commute_method_type_id admitted graduated picture
61
+ )
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,54 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class SyllabusesController < BaseController
5
+
6
+ before_action :set_syllabus, only: %i( show update destroy )
7
+
8
+ def index
9
+ @syllabuses = Syllabus.all.page(params[:page ])
10
+ collection_respond_to @syllabuses, root: :syllabuses
11
+ end
12
+
13
+ def show
14
+ member_respond_to @syllabus
15
+ end
16
+
17
+ def create
18
+ @syllabus = Syllabus.new(syllabus_params)
19
+ if @syllabus.save!
20
+ member_respond_to @syllabus
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @syllabus.update!(syllabus_params)
26
+ member_respond_to @syllabus
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ if @syllabus.destroy!
32
+ member_respond_to @syllabus
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def set_syllabus
39
+ @syllabus = Syllabus.find(params[:id])
40
+ end
41
+
42
+ def syllabus_params
43
+ params.require(syllabus_attrs)
44
+ params.permit(syllabus_attrs)
45
+ end
46
+
47
+ def syllabus_attrs
48
+ %i( name code )
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Gaku
3
+ class ClassGroupSerializer < ActiveModel::Serializer
4
+ attributes %i( id name grade homeroom notes_count faculty_id created_at updated_at enrollments_count )
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Gaku
2
+ class CourseSerializer < ActiveModel::Serializer
3
+ attributes %i( id name code notes_count students_count faculty_id
4
+ syllabus_id class_group_id created_at updated_at enrollments_count
5
+ )
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module Gaku
2
+ class EnrollmentSerializer < ActiveModel::Serializer
3
+ attributes %i( id student_id seat_number )
4
+ attribute :course_id, if: :course_enrollment?
5
+
6
+ def course_id
7
+ object.enrollable_id
8
+ end
9
+
10
+ def course_enrollment?
11
+ object.course_type?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class ExamSessionSerializer < ActiveModel::Serializer
3
+ attributes %i( id session_time name exam_id session_start enrollments_count )
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class ExtracurricularActivitySerializer < ActiveModel::Serializer
3
+ attributes %i( id name created_at updated_at enrollments_count )
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Gaku
2
+ class GuardianSerializer < ActiveModel::Serializer
3
+ attributes %i( id name surname middle_name name_reading middle_name_reading surname_reading
4
+ gender birth_date relationship picture_file_name picture_content_type picture_file_size
5
+ picture_updated_at primary_address primary_contact addresses_count contacts_count user_id
6
+ created_at updated_at
7
+ )
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Gaku
2
+ class StudentSerializer < ActiveModel::Serializer
3
+ attributes %i( id name surname middle_name name_reading middle_name_reading surname_reading
4
+ gender birth_date admitted graduated code serial_id foreign_id_code national_registration_code
5
+ enrollment_status_code picture_file_name picture_content_type picture_file_size
6
+ picture_updated_at addresses_count contacts_count notes_count courses_count guardians_count
7
+ external_school_records_count badges_count primary_address primary_contact class_and_number
8
+ user_id faculty_id commute_method_type_id scholarship_status_id created_at updated_at
9
+ extracurricular_activities_count class_groups_count )
10
+
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class SyllabusSerializer < ActiveModel::Serializer
3
+ attributes %i( id name code )
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'json_web_token'
2
+ module Gaku
3
+ module Api
4
+ class AuthenticateUser
5
+ prepend SimpleCommand
6
+
7
+ attr_accessor :username, :password
8
+
9
+ def initialize(username:, password:)
10
+ @username = username
11
+ @password = password
12
+ end
13
+
14
+ def call
15
+ if user
16
+ secret = SecureRandom.hex(64)
17
+ $redis.set("token:#{jti}", secret, ex: 20.minutes)
18
+ {
19
+ auth_token: JsonWebToken.encode({user_id: user.id}, exp: 3.minutes.from_now, jti: jti, secret: secret),
20
+ refresh_token: JsonWebToken.encode({user_id: user.id}, exp: 20.minutes.from_now, jti: jti, secret: secret)
21
+ }
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def jti
28
+ @jti ||= SecureRandom.uuid
29
+ end
30
+
31
+ def user
32
+ user = Gaku::User.find_by(username: username)
33
+ return user if user && user.valid_password?(password)
34
+
35
+ errors.add(:user_authentication, 'invalid credentials')
36
+ nil
37
+ end
38
+
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ require 'json_web_token'
2
+
3
+ module Gaku
4
+ module Api
5
+ class AuthorizeApiRequest
6
+ prepend SimpleCommand
7
+
8
+ def initialize(headers = {})
9
+ @headers = headers
10
+ end
11
+
12
+ def call
13
+ user
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :headers
19
+
20
+ def user
21
+ @user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
22
+ @user || errors.add(:token, 'Invalid token') && nil
23
+ end
24
+
25
+ def decoded_auth_token
26
+ @decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
27
+ end
28
+
29
+ def http_auth_header
30
+ if headers['Authorization'].present?
31
+ return headers['Authorization'].split(' ').last
32
+ else
33
+ errors.add(:token, 'Missing token')
34
+ end
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ require 'json_web_token'
2
+ module Gaku
3
+ module Api
4
+ class RefreshAuthenticateUser
5
+ prepend SimpleCommand
6
+
7
+ attr_accessor :refresh_token
8
+
9
+ def initialize(refresh_token)
10
+ @refresh_token = refresh_token
11
+ end
12
+
13
+ def call
14
+ if user
15
+ secret = SecureRandom.hex(64)
16
+ $redis.set("token:#{jti}", secret, ex: 20.minutes)
17
+ {
18
+ auth_token: JsonWebToken.encode({user_id: user.id}, exp: 3.minutes.from_now, jti: jti, secret: secret),
19
+ refresh_token: JsonWebToken.encode({user_id: user.id}, exp: 20.minutes.from_now, jti: jti, secret: secret)
20
+ }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def jti
27
+ @jti ||= SecureRandom.uuid
28
+ end
29
+
30
+ def user
31
+ @user ||= User.find(decoded_refresh_token[:user_id]) if decoded_refresh_token
32
+ @user || errors.add(:token, 'Invalid expire token') && nil
33
+ end
34
+
35
+ def decoded_refresh_token
36
+ @decoded_refresh_token ||= JsonWebToken.decode(refresh_token, refresh: true)
37
+ end
38
+
39
+ # def user
40
+ # user = Gaku::User.find_by(email: email)
41
+ # return user if user && user.valid_password?(password)
42
+ #
43
+ # errors.add(:user_authentication, 'invalid credentials')
44
+ # nil
45
+ # end
46
+
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ Kaminari.configure do |config|
2
+ config.default_per_page = 100
3
+ end
@@ -0,0 +1 @@
1
+ Mime::Type.register "application/msgpack", :msgpack
data/config/routes.rb ADDED
@@ -0,0 +1,31 @@
1
+ Gaku::Core::Engine.routes.draw do
2
+ namespace :api do
3
+ namespace :v1 do
4
+ resource :status
5
+
6
+ post 'authenticate', to: 'authentication#authenticate'
7
+ post 'authenticate/refresh', to: 'authentication#refresh'
8
+
9
+ resources :students do
10
+ resources :guardians, controller: 'students/guardians'
11
+ resources :student_guardians, controller: 'students/student_guardians', only: %i(create destroy)
12
+ resources :courses, controller: 'students/courses'
13
+ resources :class_groups, controller: 'students/class_groups'
14
+ resources :extracurricular_activities, controller: 'students/extracurricular_activities'
15
+ resources :exam_sessions, controller: 'students/exam_sessions'
16
+ end
17
+
18
+ resources :courses do
19
+ resources :students, controller: 'courses/students'
20
+ resources :enrollments, controller: 'courses/enrollments'
21
+ end
22
+
23
+ resources :class_groups do
24
+ resources :students, controller: 'class_groups/students'
25
+ resources :enrollments, controller: 'class_groups/enrollments'
26
+ end
27
+
28
+ resources :syllabuses
29
+ end
30
+ end
31
+ end
data/gaku_api.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ require_relative '../common_gaku_gemspec_mixin'
2
+
3
+ Gem::Specification.new do |s|
4
+ set_gaku_gemspec_shared s
5
+
6
+ s.name = 'gaku_api'
7
+ s.summary = 'GAKU Engine API module'
8
+ s.description = 'API functionality for GAKU Engine. See https://github.com/GAKUEngine/gaku'
9
+
10
+ s.files = Dir.glob("{app,config,db,lib}/**/*") +
11
+ [
12
+ 'Rakefile',
13
+ 'gaku_api.gemspec'
14
+ ]
15
+
16
+ s.add_dependency 'simple_command'
17
+ s.add_dependency 'jwt'
18
+ s.add_dependency 'active_model_serializers'
19
+ s.add_dependency 'msgpack_rails'
20
+ s.add_dependency 'kaminari'
21
+
22
+ s.add_dependency 'gaku_core', s.version
23
+
24
+ s.add_development_dependency 'gaku_testing', s.version
25
+ end
@@ -0,0 +1,28 @@
1
+ module Gaku
2
+ module Api
3
+ class Engine < ::Rails::Engine
4
+ engine_name 'gaku_api'
5
+ config.generators.api_only = true
6
+
7
+ initializer 'actionpack-msgpack_parser.configure' do
8
+ ActionDispatch::Request.parameter_parsers[:msgpack] = -> (raw_post) do
9
+ ActiveSupport::MessagePack.decode(raw_post) || {}
10
+ end
11
+ end
12
+
13
+ config.after_initialize do
14
+ ActionController.add_renderer :msgpack do |resource, options|
15
+ MessagePack::DefaultFactory.register_type(0x00, Symbol)
16
+ resource_serializer = ActiveModelSerializers::SerializableResource.new(resource, options)
17
+ self.content_type = Mime[:msgpack]
18
+ if resource_serializer.serializer?
19
+ self.response_body = resource_serializer.serializable_hash.msgpack_to_msgpack
20
+ else
21
+ self.response_body = resource.msgpack_to_msgpack
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ module Api
3
+ VERSION = '0.2.4'
4
+ end
5
+ end
data/lib/gaku/api.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'gaku_core'
2
+ require 'simple_command'
3
+ require 'jwt'
4
+ require 'active_model_serializers'
5
+ require 'msgpack_rails'
6
+ require 'kaminari'
7
+
8
+ module Gaku
9
+
10
+ module Api
11
+ end
12
+
13
+ end
14
+
15
+ require 'gaku/api/engine'
data/lib/gaku_api.rb ADDED
@@ -0,0 +1 @@
1
+ require 'gaku/api'
@@ -0,0 +1,24 @@
1
+ class JsonWebToken
2
+ class << self
3
+ def encode(payload, exp:, jti:, secret:)
4
+ payload[:exp] = exp.to_i
5
+ payload[:jti] = jti
6
+ JWT.encode(payload, secret)
7
+ end
8
+
9
+ def decode(token, refresh: false)
10
+ secret = extract_secret(token, refresh: refresh)
11
+ body = JWT.decode(token, secret)[0]
12
+ HashWithIndifferentAccess.new body
13
+ rescue
14
+ nil
15
+ end
16
+
17
+ def extract_secret(token, refresh: false)
18
+ key = JSON.parse(Base64.decode64 token.split('.')[1])['jti']
19
+ $redis.get("token:#{key}").tap do
20
+ $redis.del("token:#{key}") if refresh
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :api do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gaku_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0.pre.4
5
+ platform: ruby
6
+ authors:
7
+ - Rei Kagetsuki
8
+ - Georgi Tapalilov
9
+ - Nakaya Yukiharu
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2018-02-26 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: simple_command
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: jwt
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: active_model_serializers
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: msgpack_rails
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: kaminari
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: gaku_core
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '='
90
+ - !ruby/object:Gem::Version
91
+ version: 0.3.0.pre.4
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.3.0.pre.4
99
+ - !ruby/object:Gem::Dependency
100
+ name: gaku_testing
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '='
104
+ - !ruby/object:Gem::Version
105
+ version: 0.3.0.pre.4
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '='
111
+ - !ruby/object:Gem::Version
112
+ version: 0.3.0.pre.4
113
+ description: API functionality for GAKU Engine. See https://github.com/GAKUEngine/gaku
114
+ email: info@gakuengine.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - Rakefile
120
+ - app/controllers/gaku/api/application_controller.rb
121
+ - app/controllers/gaku/api/v1/authentication_controller.rb
122
+ - app/controllers/gaku/api/v1/base_controller.rb
123
+ - app/controllers/gaku/api/v1/class_groups/enrollments_controller.rb
124
+ - app/controllers/gaku/api/v1/class_groups/students_controller.rb
125
+ - app/controllers/gaku/api/v1/courses/enrollments_controller.rb
126
+ - app/controllers/gaku/api/v1/courses/students_controller.rb
127
+ - app/controllers/gaku/api/v1/courses_controller.rb
128
+ - app/controllers/gaku/api/v1/enrollments_controller.rb
129
+ - app/controllers/gaku/api/v1/statuses_controller.rb
130
+ - app/controllers/gaku/api/v1/students/class_groups_controller.rb
131
+ - app/controllers/gaku/api/v1/students/courses_controller.rb
132
+ - app/controllers/gaku/api/v1/students/exam_sessions_controller.rb
133
+ - app/controllers/gaku/api/v1/students/extracurricular_activities_controller.rb
134
+ - app/controllers/gaku/api/v1/students/guardians_controller.rb
135
+ - app/controllers/gaku/api/v1/students/student_guardians_controller.rb
136
+ - app/controllers/gaku/api/v1/students_controller.rb
137
+ - app/controllers/gaku/api/v1/syllabuses_controller.rb
138
+ - app/serializers/gaku/class_group_serializer.rb
139
+ - app/serializers/gaku/course_serializer.rb
140
+ - app/serializers/gaku/enrollment_serializer.rb
141
+ - app/serializers/gaku/exam_session_serializer.rb
142
+ - app/serializers/gaku/extracurricular_activity_serializer.rb
143
+ - app/serializers/gaku/guardian_serializer.rb
144
+ - app/serializers/gaku/student_serializer.rb
145
+ - app/serializers/gaku/syllabus_serializer.rb
146
+ - app/services/gaku/api/authenticate_user.rb
147
+ - app/services/gaku/api/authorize_api_request.rb
148
+ - app/services/gaku/api/refresh_authenticate_user.rb
149
+ - config/initializers/kaminari.rb
150
+ - config/initializers/mime_types.rb
151
+ - config/routes.rb
152
+ - gaku_api.gemspec
153
+ - lib/gaku/api.rb
154
+ - lib/gaku/api/engine.rb
155
+ - lib/gaku/api/version.rb
156
+ - lib/gaku_api.rb
157
+ - lib/json_web_token.rb
158
+ - lib/tasks/api_tasks.rake
159
+ homepage: http://www.gakuengine.com
160
+ licenses:
161
+ - GPL-3.0
162
+ - AGPL-3.0
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">"
176
+ - !ruby/object:Gem::Version
177
+ version: 1.3.1
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.7.3
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: GAKU Engine API module
184
+ test_files: []