summaryrefslogtreecommitdiffstats
path: root/lib/zorglub/session.rb
blob: 8162cf1aecbe59454bd40b584703dbca10dbdfca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# -*- coding: UTF-8 -*-
#
require 'securerandom'
#
module Zorglub
    #
    class Node
        #
        def session
            @session ||= Session.new @request, @response
        end
    end
    #
    class SessionHash
        #
        @data = {}
        class << self
            attr_reader :data
            def sid_exists? sid
                not @data[sid].nil?
            end
        end
        #
        attr_reader :sid
        #
        def initialize sid
            @sid = sid
            @session_data = SessionHash.data[sid]||={}
        end
        #
        def destroy!
            SessionHash.data.delete @sid
            @session_data = nil
            @sid = nil
        end
        #
        def [] idx
            @session_data[idx]
        end
        #
        def []= idx, v
            @session_data[idx] = v
        end
    end
    #
    class Session
        #
        @key =  'zorglub.sid'
        @kls = Zorglub::SessionHash
        @sid_length = 64
        #
        class << self
            attr_accessor :key, :kls, :sid_length
        end
        #
        def initialize req, resp
            @request = req
            @response = resp
            @instance = nil
        end
        #
        def setup!
            if Config.session_on
                cookie = @request.cookies[Session.key]
                if cookie.nil?
                    cookie = generate_sid
                    @response.set_cookie Session.key, cookie
                end
                @instance = Session.kls.new cookie
            end
        end
        private :setup!
        #
        def destroy!
            @response.delete_cookie Session.key
            @instance.destroy! if @instance
            @instance = nil
        end
        #
        def sid
            setup! if @instance.nil?
            return nil if @instance.nil?
            @instance.sid
        end
        #
        def [] idx
            setup! if @instance.nil?
            return nil if @instance.nil?
            @instance[idx]
        end
        #
        def []= idx, v
            setup! if @instance.nil?
            return nil if @instance.nil?
            @instance[idx] = v
        end
        #
        def generate_sid
            begin sid = sid_algorithm end while Session.kls.sid_exists? sid
            sid
        end
        #
        begin
            require 'securerandom'
            # Using SecureRandom, optional length.
            # SecureRandom is available since Ruby 1.8.7.
            # For Ruby versions earlier than that, you can require the uuidtools gem,
            # which has a drop-in replacement for SecureRandom.
            def sid_algorithm; SecureRandom.hex(Session.sid_length); end
        rescue LoadError
            require 'openssl'
            # Using OpenSSL::Random for generation, this is comparable in performance
            # with stdlib SecureRandom and also allows for optional length, it should
            # have the same behaviour as the SecureRandom::hex method of the
            # uuidtools gem.
            def sid_algorithm
                OpenSSL::Random.random_bytes(Session.sid_length / 2).unpack('H*')[0]
            end
        rescue LoadError
            # Digest::SHA2::hexdigest produces a string of length 64, although
            # collisions are not very likely, the entropy is still very low and
            # length is not optional.
            #
            # Replacing it with OS-provided random data would take a lot of code and
            # won't be as cross-platform as Ruby.
            def sid_algorithm
                entropy = [ srand, rand, Time.now.to_f, rand, $$, rand, object_id ]
                Digest::SHA2.hexdigest(entropy.join)
            end
        end
        #
    end
    #
end
#
# EOF