virgil-jwt 1.0.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,125 @@
1
+ # Copyright (C) 2015-2019 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, bytes, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ require 'base64'
36
+ require 'json'
37
+
38
+ module Virgil
39
+ module Jwt
40
+ class Bytes < Array
41
+ # Initializes a new array of bytes from specified string, which encodes binary data.
42
+ # @param str [String] String to decode.
43
+ # @param encoding [VirgilStringEncoding] The character encoding of string.
44
+ # @raise [ArgumentError] if encoding is undefined
45
+ def self.from_string(str, encoding = VirgilStringEncoding::UTF8)
46
+ case encoding
47
+ when VirgilStringEncoding::BASE64
48
+ from_base64(str)
49
+ when VirgilStringEncoding::HEX
50
+ from_hex(str)
51
+ when VirgilStringEncoding::UTF8
52
+ from_utf8(str)
53
+ else
54
+ raise ArgumentError, 'Encoding is undefined'
55
+ end
56
+ end
57
+
58
+ # Decodes the current bytes to a string according to the specified
59
+ # character encoding.
60
+ # @param encoding [VirgilStringEncoding] The character encoding to encode to.
61
+ # equivalent string representation if raw bytes in selected encoding.
62
+ # @return [String]
63
+ # @raise [ArgumentError] if encoding is undefined
64
+ def to_string(encoding = VirgilStringEncoding::UTF8)
65
+ case encoding
66
+ when VirgilStringEncoding::BASE64
67
+ to_base64
68
+ when VirgilStringEncoding::HEX
69
+ to_hex
70
+ when VirgilStringEncoding::UTF8
71
+ to_s
72
+ else
73
+ raise ArgumentError, 'Encoding is undefined'
74
+ end
75
+ end
76
+
77
+ # Converts all the bytes to its equivalent string representation in utf8.
78
+ def to_s
79
+ pack('c*')
80
+ end
81
+
82
+ # Initializes a new array of bytes from specified string,
83
+ # which encodes binary data as base-64 digits.
84
+ def self.from_base64(str)
85
+ new(Base64.decode64(str).bytes)
86
+ end
87
+
88
+ # Initializes a new array of bytes from specified string,
89
+ # which encodes binary data as utf8.
90
+ def self.from_utf8(str)
91
+ new(str.bytes)
92
+ end
93
+
94
+ # Initializes a new array of bytes from specified string,
95
+ # which encodes binary data as hexadecimal digits.
96
+ def self.from_hex(str)
97
+ new(str.scan(/../).map { |x| x.hex })
98
+ end
99
+
100
+ # Converts all the bytes to its equivalent string representation that
101
+ # is encoded with base-64 digits.
102
+ def to_base64
103
+ Base64.strict_encode64(to_s)
104
+ end
105
+
106
+ # Encodes all the bytes into a utf8 string.
107
+ def to_utf8
108
+ to_s
109
+ end
110
+
111
+ # Converts the numeric value of each element of a current array of bytes to its
112
+ # equivalent hexadecimal string representation.
113
+ def to_hex
114
+ to_s.each_byte.map { |b| b.to_s(16) }.join
115
+ end
116
+
117
+ end
118
+
119
+ module VirgilStringEncoding
120
+ BASE64 = 1
121
+ HEX = 2
122
+ UTF8 = 3
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright (C) 2015-2019 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, bytes, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+ require 'thread'
35
+
36
+ module Virgil
37
+ module Jwt
38
+
39
+ # Provides an opportunity to
40
+ # get cached access token or renew it using callback mechanism.
41
+ class CachingJwtProvider < AccessTokenProvider
42
+
43
+ # Callback, that takes an instance of [TokenContext]
44
+ # and returns string representation of generated instance of [AccessToken]
45
+ # @return [Proc]
46
+ attr_reader :renew_access_token_proc
47
+
48
+ @@mutex = Mutex.new
49
+ def initialize(obtain_token_proc)
50
+ Validation.check_type_argument!(Proc, obtain_token_proc)
51
+ @renew_access_token_proc = obtain_token_proc
52
+ end
53
+
54
+ # Gets cached or renewed access token.
55
+ # @param token_context an instance of [TokenContext]
56
+ # @return The instance of [AccessToken]
57
+ def get_token(token_context)
58
+ Validation.check_type_argument!(TokenContext, token_context)
59
+
60
+ if !@jwt || (@jwt.body_content.expires_at <= Time.at(Time.now.utc.to_i + 5).utc)
61
+ @@mutex.synchronize {
62
+ jwt_str = @renew_access_token_proc.call(token_context)
63
+ @jwt = Jwt.from(jwt_str)
64
+ }
65
+ end
66
+ @jwt
67
+ end
68
+
69
+ private
70
+
71
+ attr_reader :jwt
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright (C) 2015-2019 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, bytes, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ module Virgil
36
+ module Jwt
37
+ # provides an opportunity to
38
+ # get access token using callback mechanism.
39
+ class CallbackJwtProvider < AccessTokenProvider
40
+
41
+ # Callback, that takes an instance of [TokenContext]
42
+ # and returns string representation of generated instance of [AccessToken]
43
+ attr_reader :obtain_access_token_proc
44
+
45
+ def initialize(obtain_token_proc)
46
+ Validation.check_type_argument!(Proc, obtain_token_proc)
47
+ @obtain_access_token_proc = obtain_token_proc
48
+ end
49
+
50
+ # Gets access token.
51
+ # @param token_context [TokenContext]
52
+ # @return [AccessToken] Access token
53
+ def get_token(token_context)
54
+ Validation.check_type_argument!(TokenContext, token_context)
55
+ jwt_str = @obtain_access_token_proc.call(token_context)
56
+ Jwt.from(jwt_str)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright (C) 2015-2019 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, bytes, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ module Virgil
36
+ module Jwt
37
+ # Provides an opportunity to use constant access token.
38
+ class ConstAccessTokenProvider < AccessTokenProvider
39
+ def initialize(token)
40
+ @access_token = token
41
+ end
42
+
43
+ # Gets access token.
44
+ # @param token_context [TokenContext] can be null as
45
+ # it does not affect the result.
46
+ # @return [AccessToken]
47
+ def get_token(token_context)
48
+ access_token
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :access_token
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,132 @@
1
+ # Copyright (C) 2015-2019 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, bytes, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ module Virgil
36
+ module Jwt
37
+ # Implements [AccessToken] in terms of Virgil JWT.
38
+ class Jwt < AccessToken
39
+ # Gets a jwt body
40
+ # @return [JwtBodyContent]
41
+ attr_reader :body_content
42
+
43
+ # Gets a jwt header
44
+ # @return [JwtHeaderContent]
45
+ attr_reader :header_content
46
+
47
+ # Gets a digital signature of jwt
48
+ # @return [Bytes]
49
+ attr_reader :signature_data
50
+
51
+ # String representation of jwt without signature.
52
+ # It equals to:
53
+ # Base64.urlsafe_encode64(JWT Header) + "." + Base64.urlsafe_encode64(JWT Body)
54
+ # @return [String]
55
+ attr_reader :unsigned_data
56
+
57
+ # Initializes a new instance of the [Jwt] class using specified header,
58
+ # body and signature.
59
+ # @param header_content [JwtHeaderContext] jwt header
60
+ # @param body_content [JwtBodyContent] jwt body
61
+ # @param signature_data [Bytes] jwt signature data
62
+ def initialize(header_content:, body_content:, signature_data:)
63
+ @header_content = header_content
64
+ @body_content = body_content
65
+ @signature_data = signature_data
66
+ @string_representation = "#{header_base64}.#{body_base64}"
67
+ @unsigned_data = Bytes.from_string(@string_representation)
68
+ @string_representation += ".#{signature_base64}" unless @signature_data.nil?
69
+ end
70
+
71
+ # Initializes a new instance of the [Jwt] class using
72
+ # its string representation
73
+ # @param jwt_str [String] string representation of signed jwt.
74
+ # It must be equal to:
75
+ # Base64.urlsafe_encode64(jwt_header.to_base64) + "."
76
+ # + Base64.urlsafe_encode64(JWT Body) "."
77
+ # + Base64.urlsafe_encode64(Jwt Signature).
78
+ # @return [Jwt]
79
+ def self.from(jwt_str)
80
+ begin
81
+ parts = jwt_str.split('.')
82
+ raise ArgumentError unless parts.size == 3
83
+ signature_data = Bytes.new(Base64.urlsafe_decode64(parts[2]).bytes)
84
+ new(header_content: parse_header_content(parts[0]),
85
+ body_content: parse_body_content(parts[1]),
86
+ signature_data: signature_data)
87
+ rescue StandardError
88
+ raise ArgumentError, 'Wrong JWT format.'
89
+ end
90
+
91
+ end
92
+
93
+ # String representation of jwt.
94
+ # @return [String]
95
+ def to_s
96
+ @string_representation
97
+ end
98
+
99
+ # Whether or not token is expired.
100
+ # @return [TrueClass]
101
+ def expired?
102
+ Time.now.utc >= @body_content.expires_at
103
+ end
104
+
105
+ private
106
+
107
+ attr_reader :string_representation
108
+
109
+ def self.parse_body_content(str)
110
+ body_json = Base64.urlsafe_decode64(str)
111
+ JwtBodyContent.restore_from_json(body_json)
112
+ end
113
+
114
+ def self.parse_header_content(str)
115
+ header_json = Base64.urlsafe_decode64(str)
116
+ JwtHeaderContent.restore_from_json(header_json)
117
+ end
118
+
119
+ def header_base64
120
+ Base64.urlsafe_encode64(@header_content.to_json, padding: false)
121
+ end
122
+
123
+ def body_base64
124
+ Base64.urlsafe_encode64(@body_content.to_json, padding: false)
125
+ end
126
+
127
+ def signature_base64
128
+ Base64.urlsafe_encode64(@signature_data.to_s, padding: false)
129
+ end
130
+ end
131
+ end
132
+ end