source_license_sdk 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +43 -0
- data/Gemfile +8 -0
- data/LICENSE +674 -0
- data/README.md +329 -0
- data/Rakefile +5 -0
- data/lib/source_license_sdk/client.rb +180 -0
- data/lib/source_license_sdk/exceptions.rb +62 -0
- data/lib/source_license_sdk/license_validator.rb +103 -0
- data/lib/source_license_sdk/machine_identifier.rb +179 -0
- data/lib/source_license_sdk/version.rb +5 -0
- data/lib/source_license_sdk.rb +93 -0
- data/source_license_sdk.gemspec +42 -0
- metadata +99 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
# Generates unique machine identifiers for license activation
|
7
|
+
class SourceLicenseSDK::MachineIdentifier
|
8
|
+
class << self
|
9
|
+
# Generate a unique machine identifier
|
10
|
+
def generate
|
11
|
+
components = []
|
12
|
+
|
13
|
+
# Get hostname
|
14
|
+
components << hostname
|
15
|
+
|
16
|
+
# Get MAC addresses
|
17
|
+
components.concat(mac_addresses)
|
18
|
+
|
19
|
+
# Get CPU info if available
|
20
|
+
components << cpu_info if cpu_info
|
21
|
+
|
22
|
+
# Get motherboard info if available
|
23
|
+
components << motherboard_info if motherboard_info
|
24
|
+
|
25
|
+
# Get disk serial if available
|
26
|
+
components << disk_serial if disk_serial
|
27
|
+
|
28
|
+
# Create hash from all components
|
29
|
+
raw_id = components.compact.join('|')
|
30
|
+
Digest::SHA256.hexdigest(raw_id)[0..31] # First 32 characters
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate machine fingerprint (more detailed than machine ID)
|
34
|
+
def generate_fingerprint
|
35
|
+
components = []
|
36
|
+
|
37
|
+
# Basic system info
|
38
|
+
components << hostname
|
39
|
+
components << ruby_version
|
40
|
+
components << platform
|
41
|
+
|
42
|
+
# Network info
|
43
|
+
components.concat(mac_addresses)
|
44
|
+
components << local_ip
|
45
|
+
|
46
|
+
# Hardware info
|
47
|
+
components << cpu_info if cpu_info
|
48
|
+
components << memory_info if memory_info
|
49
|
+
components << disk_info if disk_info
|
50
|
+
|
51
|
+
# Environment info
|
52
|
+
components << environment_hash
|
53
|
+
|
54
|
+
raw_fingerprint = components.compact.join('|')
|
55
|
+
Digest::SHA256.hexdigest(raw_fingerprint)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def hostname
|
61
|
+
Socket.gethostname
|
62
|
+
rescue StandardError
|
63
|
+
'unknown-host'
|
64
|
+
end
|
65
|
+
|
66
|
+
def mac_addresses
|
67
|
+
addresses = []
|
68
|
+
|
69
|
+
case RUBY_PLATFORM
|
70
|
+
when /darwin/i
|
71
|
+
# macOS
|
72
|
+
output = `ifconfig 2>/dev/null`
|
73
|
+
addresses = output.scan(/ether ([a-f0-9:]{17})/i).flatten
|
74
|
+
when /linux/i
|
75
|
+
# Linux
|
76
|
+
output = `ip link show 2>/dev/null || ifconfig 2>/dev/null`
|
77
|
+
addresses = output.scan(/(?:ether|HWaddr)\s+([a-f0-9:]{17})/i).flatten
|
78
|
+
when /mswin|mingw|cygwin/i
|
79
|
+
# Windows
|
80
|
+
output = `getmac /fo csv /nh 2>nul`
|
81
|
+
addresses = output.scan(/"([A-F0-9-]{17})"/i).flatten.map { |addr| addr.tr('-', ':').downcase }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Filter out virtual/invalid addresses
|
85
|
+
addresses.select { |addr| addr && !addr.start_with?('00:00:00') && addr != '02:00:00:00:00:00' }
|
86
|
+
rescue StandardError
|
87
|
+
[]
|
88
|
+
end
|
89
|
+
|
90
|
+
def cpu_info
|
91
|
+
case RUBY_PLATFORM
|
92
|
+
when /darwin/i
|
93
|
+
`sysctl -n machdep.cpu.brand_string 2>/dev/null`.strip
|
94
|
+
when /linux/i
|
95
|
+
info = `cat /proc/cpuinfo 2>/dev/null | grep "model name" | head -1`.strip
|
96
|
+
info.split(':').last&.strip if info.include?(':')
|
97
|
+
when /mswin|mingw|cygwin/i
|
98
|
+
`wmic cpu get name /value 2>nul | findstr "Name="`.strip.split('=').last
|
99
|
+
end
|
100
|
+
rescue StandardError
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def motherboard_info
|
105
|
+
case RUBY_PLATFORM
|
106
|
+
when /darwin/i
|
107
|
+
`system_profiler SPHardwareDataType 2>/dev/null | grep "Serial Number"`.strip
|
108
|
+
when /linux/i
|
109
|
+
`sudo dmidecode -s baseboard-serial-number 2>/dev/null`.strip
|
110
|
+
when /mswin|mingw|cygwin/i
|
111
|
+
`wmic baseboard get serialnumber /value 2>nul | findstr "SerialNumber="`.strip.split('=').last
|
112
|
+
end
|
113
|
+
rescue StandardError
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def disk_serial
|
118
|
+
case RUBY_PLATFORM
|
119
|
+
when /darwin/i
|
120
|
+
`diskutil info / 2>/dev/null | grep "Volume UUID"`.strip.split(':').last&.strip
|
121
|
+
when /linux/i
|
122
|
+
`lsblk -dno SERIAL 2>/dev/null | head -1`.strip
|
123
|
+
when /mswin|mingw|cygwin/i
|
124
|
+
`wmic diskdrive get serialnumber /value 2>nul | findstr "SerialNumber="`.strip.split('=').last
|
125
|
+
end
|
126
|
+
rescue StandardError
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def ruby_version
|
131
|
+
RUBY_VERSION
|
132
|
+
end
|
133
|
+
|
134
|
+
def platform
|
135
|
+
RUBY_PLATFORM
|
136
|
+
end
|
137
|
+
|
138
|
+
def local_ip
|
139
|
+
# Get local IP by connecting to a remote address (doesn't actually send data)
|
140
|
+
Socket.ip_address_list.find(&:ipv4_private?)&.ip_address
|
141
|
+
rescue StandardError
|
142
|
+
'127.0.0.1'
|
143
|
+
end
|
144
|
+
|
145
|
+
def memory_info
|
146
|
+
case RUBY_PLATFORM
|
147
|
+
when /darwin/i
|
148
|
+
`sysctl -n hw.memsize 2>/dev/null`.strip
|
149
|
+
when /linux/i
|
150
|
+
`cat /proc/meminfo 2>/dev/null | grep MemTotal`.strip.split.last
|
151
|
+
when /mswin|mingw|cygwin/i
|
152
|
+
output = `wmic computersystem get TotalPhysicalMemory /value 2>nul | findstr "TotalPhysicalMemory="`
|
153
|
+
output.strip.split('=').last
|
154
|
+
end
|
155
|
+
rescue StandardError
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def disk_info
|
160
|
+
case RUBY_PLATFORM
|
161
|
+
when /darwin/i, /linux/i
|
162
|
+
`df -h / 2>/dev/null | tail -1`.strip.split.first
|
163
|
+
when /mswin|mingw|cygwin/i
|
164
|
+
`wmic logicaldisk get size,caption /value 2>nul | findstr "Size=" | head -1`.strip.split('=').last
|
165
|
+
end
|
166
|
+
rescue StandardError
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def environment_hash
|
171
|
+
# Hash of relevant environment variables (non-sensitive)
|
172
|
+
env_vars = %w[HOME USER USERNAME PATH SHELL]
|
173
|
+
env_data = env_vars.map { |var| "#{var}=#{ENV.fetch(var, nil)}" }.join('|')
|
174
|
+
Digest::SHA256.hexdigest(env_data)[0..15] # First 16 characters
|
175
|
+
rescue StandardError
|
176
|
+
'unknown-env'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'source_license_sdk/version'
|
4
|
+
require_relative 'source_license_sdk/client'
|
5
|
+
require_relative 'source_license_sdk/machine_identifier'
|
6
|
+
require_relative 'source_license_sdk/license_validator'
|
7
|
+
require_relative 'source_license_sdk/exceptions'
|
8
|
+
|
9
|
+
module SourceLicenseSDK
|
10
|
+
# Configure the SDK with your Source-License server details
|
11
|
+
def self.configure
|
12
|
+
yield(configuration)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configuration
|
16
|
+
@configuration ||= Configuration.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# Quick setup method for common use cases
|
20
|
+
def self.setup(server_url:, license_key:, machine_id: nil, auto_generate_machine_id: true)
|
21
|
+
configure do |config|
|
22
|
+
config.server_url = server_url
|
23
|
+
config.license_key = license_key
|
24
|
+
config.machine_id = machine_id
|
25
|
+
config.auto_generate_machine_id = auto_generate_machine_id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Validate license (method 1)
|
30
|
+
def self.validate_license(license_key = nil, machine_id: nil)
|
31
|
+
license_key ||= configuration.license_key
|
32
|
+
machine_id ||= configuration.machine_id
|
33
|
+
|
34
|
+
raise ConfigurationError, 'License key is required' if license_key.nil? || license_key.empty?
|
35
|
+
|
36
|
+
client = Client.new(configuration)
|
37
|
+
client.validate_license(license_key, machine_id: machine_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Activate license (method 2)
|
41
|
+
def self.activate_license(license_key = nil, machine_id: nil)
|
42
|
+
license_key ||= configuration.license_key
|
43
|
+
machine_id ||= configuration.machine_id ||
|
44
|
+
(configuration.auto_generate_machine_id ? MachineIdentifier.generate : nil)
|
45
|
+
|
46
|
+
raise ConfigurationError, 'License key is required' if license_key.nil? || license_key.empty?
|
47
|
+
raise ConfigurationError, 'Machine ID is required for activation' if machine_id.nil? || machine_id.empty?
|
48
|
+
|
49
|
+
client = Client.new(configuration)
|
50
|
+
client.activate_license(license_key, machine_id: machine_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Enforce license check - exits application if license is invalid (method 3)
|
54
|
+
def self.enforce_license!(license_key = nil, machine_id: nil, exit_code: 1, custom_message: nil)
|
55
|
+
license_key ||= configuration.license_key
|
56
|
+
machine_id ||= configuration.machine_id
|
57
|
+
|
58
|
+
begin
|
59
|
+
result = validate_license(license_key, machine_id: machine_id)
|
60
|
+
|
61
|
+
unless result.valid?
|
62
|
+
message = custom_message || "License validation failed: #{result.error_message}"
|
63
|
+
puts "[LICENSE ERROR] #{message}"
|
64
|
+
exit(exit_code)
|
65
|
+
end
|
66
|
+
|
67
|
+
result
|
68
|
+
rescue SourceLicenseSDK::Error => e
|
69
|
+
message = custom_message || "License check failed: #{e.message}"
|
70
|
+
puts "[LICENSE ERROR] #{message}"
|
71
|
+
exit(exit_code)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Configuration
|
76
|
+
attr_accessor :server_url, :license_key, :machine_id, :auto_generate_machine_id,
|
77
|
+
:timeout, :user_agent, :verify_ssl
|
78
|
+
|
79
|
+
def initialize
|
80
|
+
@server_url = nil
|
81
|
+
@license_key = nil
|
82
|
+
@machine_id = nil
|
83
|
+
@auto_generate_machine_id = true
|
84
|
+
@timeout = 30
|
85
|
+
@user_agent = "SourceLicenseSDK/#{VERSION}"
|
86
|
+
@verify_ssl = true
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid?
|
90
|
+
!server_url.nil? && !server_url.empty?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/source_license_sdk/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'source_license_sdk'
|
7
|
+
spec.version = SourceLicenseSDK::VERSION
|
8
|
+
spec.authors = ['PixelRidge Softworks']
|
9
|
+
spec.email = ['support@pixelridgesoftworks.com']
|
10
|
+
|
11
|
+
spec.summary = 'Ruby SDK for Source-License platform'
|
12
|
+
spec.description = 'A Ruby gem for easy integration with Source-License platform for license ' \
|
13
|
+
'validation and activation'
|
14
|
+
spec.homepage = 'https://github.com/PixelRidgeSoftworks/Source-License'
|
15
|
+
spec.license = 'GPL-3.0-or-later'
|
16
|
+
spec.required_ruby_version = '>= 3.4.4'
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/PixelRidgeSoftworks/Source-License/tree/main/SL_Ruby_SDK'
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/PixelRidgeSoftworks/Source-License/blob/main/SL_Ruby_SDK/CHANGELOG.md'
|
21
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# Use cross-platform compatible file detection
|
25
|
+
spec.files = Dir.glob(%w[
|
26
|
+
lib/**/*.rb
|
27
|
+
README.md
|
28
|
+
CHANGELOG.md
|
29
|
+
LICENSE
|
30
|
+
Gemfile
|
31
|
+
Rakefile
|
32
|
+
source_license_sdk.gemspec
|
33
|
+
]).select { |f| File.file?(f) }
|
34
|
+
spec.bindir = 'exe'
|
35
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
36
|
+
spec.require_paths = ['lib']
|
37
|
+
|
38
|
+
# Dependencies
|
39
|
+
spec.add_dependency 'digest', '~> 3.0'
|
40
|
+
spec.add_dependency 'json', '~> 2.0'
|
41
|
+
spec.add_dependency 'net-http', '~> 0.1'
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: source_license_sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- PixelRidge Softworks
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: digest
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: net-http
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.1'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.1'
|
54
|
+
description: A Ruby gem for easy integration with Source-License platform for license
|
55
|
+
validation and activation
|
56
|
+
email:
|
57
|
+
- support@pixelridgesoftworks.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- CHANGELOG.md
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/source_license_sdk.rb
|
68
|
+
- lib/source_license_sdk/client.rb
|
69
|
+
- lib/source_license_sdk/exceptions.rb
|
70
|
+
- lib/source_license_sdk/license_validator.rb
|
71
|
+
- lib/source_license_sdk/machine_identifier.rb
|
72
|
+
- lib/source_license_sdk/version.rb
|
73
|
+
- source_license_sdk.gemspec
|
74
|
+
homepage: https://github.com/PixelRidgeSoftworks/Source-License
|
75
|
+
licenses:
|
76
|
+
- GPL-3.0-or-later
|
77
|
+
metadata:
|
78
|
+
homepage_uri: https://github.com/PixelRidgeSoftworks/Source-License
|
79
|
+
source_code_uri: https://github.com/PixelRidgeSoftworks/Source-License/tree/main/SL_Ruby_SDK
|
80
|
+
changelog_uri: https://github.com/PixelRidgeSoftworks/Source-License/blob/main/SL_Ruby_SDK/CHANGELOG.md
|
81
|
+
rubygems_mfa_required: 'true'
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.4.4
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubygems_version: 3.7.2
|
97
|
+
specification_version: 4
|
98
|
+
summary: Ruby SDK for Source-License platform
|
99
|
+
test_files: []
|