ru.Bee 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ require_relative File.join(__dir__, 'middlewarable')
2
+ require 'date'
3
+
4
+ module Rubee
5
+ module AuthTokenable
6
+ KEY = "secret#{Date.today}".freeze # Feel free to cusomtize it
7
+ EXPIRE = 3600 # 1 hour
8
+
9
+ def self.included(base)
10
+ base.include(Middlewarable)
11
+ base.include(InstanceMethods)
12
+ base.extend(ClassMethods)
13
+
14
+ base.attach('AuthTokenMiddleware')
15
+ end
16
+
17
+ module InstanceMethods
18
+ def authentificated?
19
+ methods = self.class._auth_methods
20
+ return true if methods && !methods.include?(@route[:action].to_sym)
21
+ # This is suppose to be set in the middleware, otherwise it will return false
22
+ valid_token?
23
+ end
24
+
25
+ def valid_token?
26
+ @request.env["rack.session"]&.[]("authentificated")
27
+ end
28
+
29
+ def authentificated_user
30
+ # User model must be created with email and password properties at least
31
+ @authehtificated_user ||= User.where(email: params[:email], password: params[:password]).first
32
+ end
33
+
34
+ def authentificate!
35
+ return false unless authentificated_user
36
+ # Generate token
37
+ payload = { username: params[:email], exp: Time.now.to_i + EXPIRE }
38
+ @token = JWT.encode(payload, KEY, 'HS256')
39
+ # Set jwt token to the browser within cookie, so next browser request will include it.
40
+ # make sure it passed to response_with headers options
41
+ @token_header = { "set-cookie" => "jwt=#{@token}; path=/; httponly; secure" }
42
+
43
+ true
44
+ end
45
+
46
+ def unauthentificate!
47
+ @request.env["rack.session"]["authentificated"] = nil if @request.env["rack.session"]&.[]("authentificated")
48
+ @authehtificated_user = nil if @authehtificated_user
49
+ @zeroed_token_header = {
50
+ "set-cookie" => "jwt=; path=/; httponly; secure; expires=thu, 01 jan 1970 00:00:00 gmt",
51
+ "content-type" => "application/json"
52
+ }
53
+
54
+ true
55
+ end
56
+
57
+ def handle_auth
58
+ if authentificated?
59
+ yield
60
+ else
61
+ response_with type: :unauthentificated
62
+ end
63
+ end
64
+ end
65
+
66
+ module ClassMethods
67
+ def auth_methods(*args)
68
+ @auth_methods ||= []
69
+ @auth_methods.concat(args.map(&:to_sym)).uniq!
70
+
71
+ @auth_methods.each do |method|
72
+ around method, :handle_auth
73
+ end
74
+ end
75
+
76
+ def _auth_methods
77
+ @auth_methods || []
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,31 @@
1
+ # Rubee system file
2
+ # WARNING: DO NOT EDIT THIS FILE UNLESS YOU FEEL STRONG DESIRE
3
+ # Unpreditable behaviour may happen. Take it as your own risk.
4
+ module Middlewarable
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.prepend(Initializer)
8
+ end
9
+
10
+ module Initializer
11
+ def initialize(req, route)
12
+ app = ->(env) { super(req, route) }
13
+ self.class.middlewares.reverse_each do |middleware|
14
+ middleware_class = Object.const_get(middleware)
15
+ app = middleware_class.new(app, req)
16
+ end
17
+ app.call(req.env)
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def attach(*args)
23
+ @middlewares ||= []
24
+ @middlewares.concat(args).uniq
25
+ end
26
+
27
+ def middlewares
28
+ @middlewares || []
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module Rubee
2
+ class AuthTokenMiddleware
3
+ def initialize(app, req)
4
+ @req = req
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ # get token from header
10
+ auth_header = headers(env)["HTTP_AUTHORIZATION"]
11
+ token = auth_header ? auth_header[/^Bearer (.*)$/]&.gsub("Bearer ", "") : nil
12
+ # get token from cookies
13
+ token = @req.cookies["jwt"] unless token
14
+ if valid_token?(token)
15
+ env["rack.session"] ||= {}
16
+ env["rack.session"]["authentificated"] = true
17
+ end
18
+
19
+ @app.call(env)
20
+ end
21
+
22
+ private
23
+
24
+ def headers(env)
25
+ env.each_with_object({}) { |(k, v), h| h[k] = v if k.start_with?("HTTP_") }
26
+ end
27
+
28
+ def valid_token?(token)
29
+ return false unless token
30
+
31
+ hash = decode_jwt(token)
32
+ email = hash[:username]
33
+
34
+ User.where(email:)&.any? if email
35
+ end
36
+
37
+ def decode_jwt(token)
38
+ decoded_array = JWT.decode(token, AuthTokenable::KEY, true, { algorithm: 'HS256' }) rescue decoded_array = []
39
+ decoded_array&.first&.transform_keys(&:to_sym) || {} # Extract payload
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,85 @@
1
+ module Rubee
2
+ module Hookable
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.include(InstanceMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def before(method, handler, **options)
10
+ hooks = Module.new do
11
+ define_method(method) do |*args, &block|
12
+ if conditions_met?(options[:if], options[:unless])
13
+ handler.respond_to?(:call) ? handler.call : send(handler)
14
+ end
15
+
16
+ super(*args, &block)
17
+ end
18
+ end
19
+ prepend hooks
20
+ end
21
+
22
+ def after(method, handler, **options)
23
+ hooks = Module.new do
24
+ define_method(method) do |*args, &block|
25
+ result = super(*args, &block)
26
+
27
+ if conditions_met?(options[:if], options[:unless])
28
+ handler.respond_to?(:call) ? handler.call : send(handler)
29
+ end
30
+
31
+ result
32
+ end
33
+ end
34
+ prepend hooks
35
+ end
36
+
37
+ def around(method, handler, **options)
38
+ hooks = Module.new do
39
+ define_method(method) do |*args, &block|
40
+ if conditions_met?(options[:if], options[:unless])
41
+ if handler.respond_to?(:call)
42
+ handler.call { super(*args, &block) }
43
+ else
44
+ send(handler) { super(*args, &block) }
45
+ end
46
+ else
47
+ super(*args, &block)
48
+ end
49
+ end
50
+ end
51
+ prepend hooks
52
+ end
53
+ end
54
+
55
+ module InstanceMethods
56
+ private
57
+
58
+ def conditions_met?(if_condition = nil, unless_condition = nil)
59
+ return true if if_condition.nil? && unless_condition.nil?
60
+
61
+ if_condition_result = true
62
+ if_condition_result =
63
+ if if_condition.nil?
64
+ true
65
+ elsif if_condition.respond_to?(:call)
66
+ if_condition.call
67
+ elsif respond_to?(if_condition)
68
+ send(if_condition)
69
+ end
70
+
71
+ unless_condition_result = true
72
+ unless_condition_result =
73
+ if unless_condition.nil?
74
+ false
75
+ elsif unless_condition.respond_to?(:call)
76
+ unless_condition.call
77
+ elsif respond_to?(unless_condition)
78
+ send(unless_condition)
79
+ end
80
+
81
+ if_condition_result && !unless_condition_result
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,28 @@
1
+ module Rubee
2
+ module Serializable
3
+ def self.included(base)
4
+ base.send(:include, InstanceMethods)
5
+ base.prepend(Initializer)
6
+ end
7
+
8
+ module Initializer
9
+ def initialize(attrs)
10
+ attrs.each do |attr, value|
11
+ self.send("#{attr}=", value)
12
+ end
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def to_json
18
+ to_h.to_json
19
+ end
20
+
21
+ def to_h
22
+ instance_variables.each_with_object({}) do |var, hash|
23
+ hash[var.to_s.delete("@")] = instance_variable_get(var)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,50 @@
1
+ module Rubee
2
+ class DatabaseObject
3
+ include Serializable
4
+ include Hookable
5
+
6
+ def destroy
7
+ end
8
+
9
+ def save
10
+ end
11
+
12
+ def update(args = {})
13
+ end
14
+
15
+ def reload
16
+ end
17
+
18
+ class << self
19
+ def last
20
+ end
21
+
22
+ def connection
23
+ end
24
+
25
+ def all
26
+ end
27
+
28
+ def find(id)
29
+ end
30
+
31
+ def where(args)
32
+ end
33
+
34
+ def create(attrs)
35
+ end
36
+
37
+ def pluralize_class_name
38
+ word = self.name.downcase
39
+ # Basic pluralization rules
40
+ if word.end_with?('y') && !%w[a e i o u].include?(word[-2])
41
+ word[0..-2] + 'ies' # Replace "y" with "ies"
42
+ elsif word.end_with?('s', 'x', 'z', 'ch', 'sh')
43
+ word + 'es' # Add "es" for certain endings
44
+ else
45
+ word + 's' # Default to adding "s"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,86 @@
1
+ module Rubee
2
+ class SequelObject < DatabaseObject
3
+ DB = Sequel.connect(Rubee::Configuration.get_database_url) rescue nil
4
+
5
+ def destroy
6
+ self.class.connection.where(id:).delete
7
+ end
8
+
9
+ def save
10
+ args = to_h.dup&.transform_keys(&:to_sym)
11
+ if args[:id]
12
+ udpate(args) rescue return false
13
+
14
+ true
15
+ else
16
+ created_object = self.class.create(args) rescue return false
17
+ self.id = created_object.id
18
+
19
+ true
20
+ end
21
+ end
22
+
23
+ def assign_attributes(args={})
24
+ to_h.each do |attr, value|
25
+ self.send("#{attr}=", args[attr.to_sym]) if args[attr.to_sym]
26
+ end
27
+ end
28
+
29
+ def update(args = {})
30
+ assign_attributes(args)
31
+ found_hash = self.class.connection.where(id:)
32
+ return self.class.find(id) if found_hash&.update(**args)
33
+
34
+ false
35
+ end
36
+
37
+ def persisted?
38
+ !!id
39
+ end
40
+
41
+ def reload
42
+ self.class.find(id)
43
+ end
44
+
45
+ class << self
46
+ def last
47
+ found_hash = connection.order(:id).last
48
+ return self.new(**found_hash) if found_hash
49
+
50
+ nil
51
+ end
52
+
53
+ def connection
54
+ @connection ||= DB[pluralize_class_name.to_sym]
55
+ end
56
+
57
+ def all
58
+ connection.map do |record_hash|
59
+ self.new(**record_hash)
60
+ end
61
+ end
62
+
63
+ def find(id)
64
+ found_hash = connection.where(id:)&.first
65
+ return self.new(**found_hash) if found_hash
66
+
67
+ nil
68
+ end
69
+
70
+ def where(args)
71
+ connection.where(**args).map do |record_hash|
72
+ self.new(**record_hash)
73
+ end
74
+ end
75
+
76
+ def create(attrs)
77
+ out_id = connection.insert(**attrs)
78
+ self.new(**(attrs.merge(id: out_id)))
79
+ end
80
+
81
+ def destroy_all
82
+ all.each(&:destroy)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'test_helper'
2
+
3
+ class RubeeAppTest < Minitest::Test
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Rubee::Application.instance
8
+ end
9
+
10
+ def setup
11
+ Rubee::Autoload.call
12
+ end
13
+
14
+ def teardown
15
+ # detach auth methods
16
+ if WelcomeController.instance_variable_defined?(:@auth_methods)
17
+ WelcomeController.send(:remove_instance_variable, :@auth_methods)
18
+ end
19
+ end
20
+
21
+ def test_welcome_controller_included_auth_tokenable
22
+ WelcomeController.include(AuthTokenable)
23
+ WelcomeController.auth_methods :show
24
+
25
+ get '/'
26
+
27
+ assert_equal last_response.status, 401
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'test_helper'
2
+
3
+ class RubeeAppTest < Minitest::Test
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Rubee::Application.instance
8
+ end
9
+
10
+
11
+ def test_welcome_route
12
+ get '/'
13
+
14
+ assert_equal 200, last_response.status, "Unexpected response: #{last_response.body}"
15
+ assert_includes last_response.body, 'All set up and running!'
16
+ end
17
+
18
+
19
+ def test_not_found_route
20
+ get '/random'
21
+
22
+ assert_equal 404, last_response.status
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require_relative File.join(File.expand_path(Dir.pwd), 'lib', 'rubee.rb')
2
+
3
+ require "bundler/setup"
4
+ Bundler.require(:default, :test, :development)
5
+
6
+ require 'minitest/autorun'
7
+ require 'rack/test'
8
+
9
+ Rubee::Autoload.call
10
+
11
+
@@ -0,0 +1,51 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe 'User model' do
4
+ describe ".create" do
5
+ after do
6
+ User.destroy_all
7
+ end
8
+
9
+ describe 'when data is valid' do
10
+ it 'persists to db' do
11
+ user = User.create(email: "ok-test@test.com", password: "123")
12
+
13
+ _(user.persisted?).must_equal true
14
+ end
15
+ end
16
+
17
+ describe 'when data is invalid' do
18
+ it 'is not changing users number' do
19
+ initial_count = User.all.count
20
+ User.create(wrong: "test@test") rescue nil
21
+
22
+ _(User.all.count).must_equal initial_count
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '.save' do
28
+ after do
29
+ User.destroy_all
30
+ end
31
+
32
+ describe 'when data is valid' do
33
+ it 'persists to db' do
34
+ user = User.new(email: "ok-test@test.com", password: "123")
35
+ user.save
36
+
37
+ _(user.persisted?).must_equal true
38
+ end
39
+ end
40
+
41
+ describe 'when data is invalid' do
42
+ it 'is not changing users number' do
43
+ initial_count = User.all.count
44
+ user = User.new(wrong: "test@test") rescue nil
45
+ user.save rescue nil
46
+
47
+ _(User.all.count).must_equal initial_count
48
+ end
49
+ end
50
+ end
51
+ end