bluedoc-license 1.2.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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4fc049f5457a7af6e3a05758bed8369d787d199c9ac2ef45a0e6e21350127c59
4
+ data.tar.gz: f2373552d83d44f35473b3c5319300b106b278bf23ac9bebe9eec6c21a7c1244
5
+ SHA512:
6
+ metadata.gz: '0269c8d3b4ef9b7138725982aa7ffe5f933f79027103e574b89949def4c2f65ff8b874e5a60fa813dfd1771e898ec36f53acd643ade77c1926f8590b513869cc'
7
+ data.tar.gz: a7cb8605a08201bee63fe23804be1a01f795b7145182d43f6dbee95b893e7ba87c8805630d7162d973c4a51fe553a11f598d78fbc706c235854187880acdb26b
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "date"
5
+ require "json"
6
+ require "base64"
7
+
8
+ require "bluedoc/license/version"
9
+ require "bluedoc/license/encryptor"
10
+ require "bluedoc/license/boundary"
11
+
12
+ module BlueDoc
13
+ class License
14
+ class Error < StandardError; end
15
+ class ImportError < Error; end
16
+ class ValidationError < Error; end
17
+
18
+ class << self
19
+ attr_reader :encryption_key
20
+ @encryption_key = nil
21
+
22
+ def encryption_key=(key)
23
+ if key && !key.is_a?(OpenSSL::PKey::RSA)
24
+ raise ArgumentError, "No RSA encryption key provided."
25
+ end
26
+
27
+ @encryption_key = key
28
+ @encryptor = nil
29
+ end
30
+
31
+ def encryptor
32
+ @encryptor ||= Encryptor.new(self.encryption_key)
33
+ end
34
+
35
+ def import(data)
36
+ if data.nil?
37
+ raise ImportError, "No license data."
38
+ end
39
+
40
+ data = Boundary.remove_boundary(data)
41
+
42
+ begin
43
+ license_json = encryptor.decrypt(data)
44
+ rescue Encryptor::Error
45
+ raise ImportError, "License data could not be decrypted."
46
+ end
47
+
48
+ begin
49
+ attributes = JSON.parse(license_json)
50
+ rescue JSON::ParseError
51
+ raise ImportError, "License data is invalid JSON."
52
+ end
53
+
54
+ new(attributes)
55
+ end
56
+ end
57
+
58
+ attr_reader :version
59
+ attr_accessor :licensee, :starts_at, :expires_at
60
+ attr_accessor :restrictions, :features
61
+
62
+ def initialize(attributes = {})
63
+ load_attributes(attributes)
64
+ end
65
+
66
+ def valid?
67
+ return false if !licensee || !licensee.is_a?(Hash) || licensee.length == 0
68
+ return false if !starts_at || !starts_at.is_a?(Date)
69
+ return false if expires_at && !expires_at.is_a?(Date)
70
+ return false if restrictions && !restrictions.is_a?(Hash)
71
+ return false if features && !features.is_a?(Array)
72
+
73
+ true
74
+ end
75
+
76
+ def validate!
77
+ raise ValidationError, "License is invalid" unless valid?
78
+ end
79
+
80
+ def will_expire?
81
+ self.expires_at == nil ? false : true
82
+ end
83
+
84
+ def expired?
85
+ will_expire? && Date.today >= self.expires_at
86
+ end
87
+
88
+ def restricted?(key = nil)
89
+ if key
90
+ restricted? && restrictions.has_key?(key)
91
+ else
92
+ restrictions && restrictions.length >= 1
93
+ end
94
+ end
95
+
96
+ def allow_feature?(key)
97
+ self.features.include?(key.to_s)
98
+ end
99
+
100
+ private
101
+
102
+ def load_attributes(attributes)
103
+ attributes = Hash[attributes.map { |k, v| [k.to_s, v] }]
104
+
105
+ version = attributes["version"] || 1
106
+ unless version && version == 1
107
+ raise ArgumentError, "Version is too new"
108
+ end
109
+
110
+ @version = version
111
+
112
+ @licensee = attributes["licensee"]
113
+ @features = attributes["features"] || []
114
+
115
+ %w(starts_at expires_at).each do |attr|
116
+ value = attributes[attr]
117
+ value = Date.parse(value) rescue nil if value.is_a?(String)
118
+
119
+ next unless value
120
+
121
+ send("#{attr}=", value)
122
+ end
123
+
124
+ restrictions = attributes["restrictions"]
125
+ if restrictions && restrictions.is_a?(Hash)
126
+ restrictions = Hash[restrictions.map { |k, v| [k.to_sym, v] }]
127
+ @restrictions = restrictions
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueDoc
4
+ class License
5
+ module Boundary
6
+ BOUNDARY_START = /(\A|\r?\n)-*BEGIN .+? LICENSE-*\r?\n/.freeze
7
+ BOUNDARY_END = /\r?\n-*END .+? LICENSE-*(\r?\n|\z)/.freeze
8
+
9
+ class << self
10
+ def add_boundary(data, product_name)
11
+ data = remove_boundary(data)
12
+
13
+ product_name.upcase!
14
+
15
+ pad = lambda do |message, width|
16
+ total_padding = [width - message.length, 0].max
17
+
18
+ padding = total_padding / 2.0
19
+ [
20
+ "-" * padding.ceil,
21
+ message,
22
+ "-" * padding.floor
23
+ ].join
24
+ end
25
+
26
+ [
27
+ pad.call("BEGIN #{product_name} LICENSE", 60),
28
+ data.strip,
29
+ pad.call("END #{product_name} LICENSE", 60)
30
+ ].join("\n")
31
+ end
32
+
33
+ def remove_boundary(data)
34
+ after_boundary = data.split(BOUNDARY_START).last
35
+ in_boundary = after_boundary.split(BOUNDARY_END).first
36
+
37
+ in_boundary
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueDoc
4
+ class License
5
+ class Encryptor
6
+ class Error < StandardError; end
7
+ class KeyError < Error; end
8
+ class DecryptionError < Error; end
9
+
10
+ attr_accessor :key
11
+
12
+ def initialize(key)
13
+ if key && !key.is_a?(OpenSSL::PKey::RSA)
14
+ raise KeyError, "No RSA encryption key provided."
15
+ end
16
+
17
+ @key = key
18
+ end
19
+
20
+ def decrypt(data)
21
+ unless key.public?
22
+ raise KeyError, "Provided key is not a public key."
23
+ end
24
+
25
+ json_data = Base64.decode64(data.chomp)
26
+
27
+ begin
28
+ encryption_data = JSON.parse(json_data)
29
+ rescue JSON::ParserError
30
+ raise DecryptionError, "Encryption data is invalid JSON."
31
+ end
32
+
33
+ unless %w(data key iv).all? { |key| encryption_data[key] }
34
+ raise DecryptionError, "Required field missing from encryption data."
35
+ end
36
+
37
+ encrypted_data = Base64.decode64(encryption_data["data"])
38
+ encrypted_key = Base64.decode64(encryption_data["key"])
39
+ aes_iv = Base64.decode64(encryption_data["iv"])
40
+
41
+ begin
42
+ # Decrypt the AES key using asymmetric RSA encryption.
43
+ aes_key = self.key.public_decrypt(encrypted_key)
44
+ rescue OpenSSL::PKey::RSAError
45
+ raise DecryptionError, "AES encryption key could not be decrypted."
46
+ end
47
+
48
+ # Decrypt the data using symmetric AES encryption.
49
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
50
+ cipher.decrypt
51
+
52
+ begin
53
+ cipher.key = aes_key
54
+ rescue OpenSSL::Cipher::CipherError
55
+ raise DecryptionError, "AES encryption key is invalid."
56
+ end
57
+
58
+ begin
59
+ cipher.iv = aes_iv
60
+ rescue OpenSSL::Cipher::CipherError
61
+ raise DecryptionError, "AES IV is invalid."
62
+ end
63
+
64
+ begin
65
+ data = cipher.update(encrypted_data) + cipher.final
66
+ rescue OpenSSL::Cipher::CipherError
67
+ raise DecryptionError, "Data could not be decrypted."
68
+ end
69
+
70
+ data
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueDoc
4
+ class License
5
+ VERSION = "1.2.0"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bluedoc-license
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jason Lee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - huacnlee@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/bluedoc-license.rb
63
+ - lib/bluedoc/license/boundary.rb
64
+ - lib/bluedoc/license/encryptor.rb
65
+ - lib/bluedoc/license/version.rb
66
+ homepage: https://bluedoc.org
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.0.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: bluedoc-license helps you generate, verify and enforce software licenses.
89
+ test_files: []