bluedoc-license 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []