Initial commit

This commit is contained in:
Lakr Aream 2022-01-24 16:39:53 +08:00 committed by Lakr Aream
commit 00000000f4
9 changed files with 581 additions and 0 deletions

56
.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Used by dotenv library to load environment variables.
# .env
# Ignore Byebug command history file.
.byebug_history
## Specific to RubyMotion:
.dat*
.repl_history
build/
*.bridgesupport
build-iPhoneOS/
build-iPhoneSimulator/
## Specific to RubyMotion (use of CocoaPods):
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
# .rubocop-https?--*

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# GitLab-License-Generator
Generator GitLab License For Self-Hosted/Private Instances

10
generate_keys.rb Normal file
View File

@ -0,0 +1,10 @@
require 'openssl'
key_pair = OpenSSL::PKey::RSA.generate(2048)
File.open("license_key", "w") { |f| f.write(key_pair.to_pem) }
public_key = key_pair.public_key
File.open("license_key.pub", "w") { |f| f.write(public_key.to_pem) }
puts "Generated RSA key pairs, use generate_licenses.rb to generate licenses."
puts "Make your own customization to the code if needed."

432
generate_licenses.rb Normal file
View File

@ -0,0 +1,432 @@
require 'openssl'
require 'date'
require 'json'
require 'base64'
module Gitlab
class License
VERSION = '2.1.0'.freeze
end
end
module Gitlab
class License
class Encryptor
class Error < StandardError; end
class KeyError < Error; end
class DecryptionError < Error; end
attr_accessor :key
def initialize(key)
raise KeyError, 'No RSA encryption key provided.' if key && !key.is_a?(OpenSSL::PKey::RSA)
@key = key
end
def encrypt(data)
raise KeyError, 'Provided key is not a private key.' unless key.private?
# Encrypt the data using symmetric AES encryption.
cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher.encrypt
aes_key = cipher.random_key
aes_iv = cipher.random_iv
encrypted_data = cipher.update(data) + cipher.final
# Encrypt the AES key using asymmetric RSA encryption.
encrypted_key = key.private_encrypt(aes_key)
encryption_data = {
'data' => Base64.encode64(encrypted_data),
'key' => Base64.encode64(encrypted_key),
'iv' => Base64.encode64(aes_iv)
}
json_data = JSON.dump(encryption_data)
Base64.encode64(json_data)
end
def decrypt(data)
raise KeyError, 'Provided key is not a public key.' unless key.public?
json_data = Base64.decode64(data.chomp)
begin
encryption_data = JSON.parse(json_data)
rescue JSON::ParserError
raise DecryptionError, 'Encryption data is invalid JSON.'
end
unless %w[data key iv].all? { |key| encryption_data[key] }
raise DecryptionError, 'Required field missing from encryption data.'
end
encrypted_data = Base64.decode64(encryption_data['data'])
encrypted_key = Base64.decode64(encryption_data['key'])
aes_iv = Base64.decode64(encryption_data['iv'])
begin
# Decrypt the AES key using asymmetric RSA encryption.
aes_key = self.key.public_decrypt(encrypted_key)
rescue OpenSSL::PKey::RSAError
raise DecryptionError, 'AES encryption key could not be decrypted.'
end
# Decrypt the data using symmetric AES encryption.
cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher.decrypt
begin
cipher.key = aes_key
rescue OpenSSL::Cipher::CipherError
raise DecryptionError, 'AES encryption key is invalid.'
end
begin
cipher.iv = aes_iv
rescue OpenSSL::Cipher::CipherError
raise DecryptionError, 'AES IV is invalid.'
end
begin
data = cipher.update(encrypted_data) + cipher.final
rescue OpenSSL::Cipher::CipherError
raise DecryptionError, 'Data could not be decrypted.'
end
data
end
end
end
end
module Gitlab
class License
module Boundary
BOUNDARY_START = /(\A|\r?\n)-*BEGIN .+? LICENSE-*\r?\n/.freeze
BOUNDARY_END = /\r?\n-*END .+? LICENSE-*(\r?\n|\z)/.freeze
class << self
def add_boundary(data, product_name)
data = remove_boundary(data)
product_name.upcase!
pad = lambda do |message, width|
total_padding = [width - message.length, 0].max
padding = total_padding / 2.0
[
'-' * padding.ceil,
message,
'-' * padding.floor
].join
end
[
pad.call("BEGIN #{product_name} LICENSE", 60),
data.strip,
pad.call("END #{product_name} LICENSE", 60)
].join("\n")
end
def remove_boundary(data)
after_boundary = data.split(BOUNDARY_START).last
in_boundary = after_boundary.split(BOUNDARY_END).first
in_boundary
end
end
end
end
end
module Gitlab
class License
class Error < StandardError; end
class ImportError < Error; end
class ValidationError < Error; end
class << self
attr_reader :encryption_key
@encryption_key = nil
def encryption_key=(key)
raise ArgumentError, 'No RSA encryption key provided.' if key && !key.is_a?(OpenSSL::PKey::RSA)
@encryption_key = key
@encryptor = nil
end
def encryptor
@encryptor ||= Encryptor.new(encryption_key)
end
def import(data)
raise ImportError, 'No license data.' if data.nil?
data = Boundary.remove_boundary(data)
begin
license_json = encryptor.decrypt(data)
rescue Encryptor::Error
raise ImportError, 'License data could not be decrypted.'
end
begin
attributes = JSON.parse(license_json)
rescue JSON::ParseError
raise ImportError, 'License data is invalid JSON.'
end
new(attributes)
end
end
attr_reader :version
attr_accessor :licensee, :starts_at, :expires_at, :notify_admins_at,
:notify_users_at, :block_changes_at, :last_synced_at, :next_sync_at,
:activated_at, :restrictions, :cloud_licensing_enabled,
:offline_cloud_licensing_enabled, :auto_renew_enabled, :seat_reconciliation_enabled,
:operational_metrics_enabled, :generated_from_customers_dot
alias_method :issued_at, :starts_at
alias_method :issued_at=, :starts_at=
def initialize(attributes = {})
load_attributes(attributes)
end
def valid?
if !licensee || !licensee.is_a?(Hash) || licensee.empty?
false
elsif !starts_at || !starts_at.is_a?(Date)
false
elsif expires_at && !expires_at.is_a?(Date)
false
elsif notify_admins_at && !notify_admins_at.is_a?(Date)
false
elsif notify_users_at && !notify_users_at.is_a?(Date)
false
elsif block_changes_at && !block_changes_at.is_a?(Date)
false
elsif last_synced_at && !last_synced_at.is_a?(DateTime)
false
elsif next_sync_at && !next_sync_at.is_a?(DateTime)
false
elsif activated_at && !activated_at.is_a?(DateTime)
false
elsif restrictions && !restrictions.is_a?(Hash)
false
elsif !cloud_licensing? && offline_cloud_licensing?
false
else
true
end
end
def validate!
raise ValidationError, 'License is invalid' unless valid?
end
def will_expire?
expires_at
end
def will_notify_admins?
notify_admins_at
end
def will_notify_users?
notify_users_at
end
def will_block_changes?
block_changes_at
end
def will_sync?
next_sync_at
end
def activated?
activated_at
end
def expired?
will_expire? && Date.today >= expires_at
end
def notify_admins?
will_notify_admins? && Date.today >= notify_admins_at
end
def notify_users?
will_notify_users? && Date.today >= notify_users_at
end
def block_changes?
will_block_changes? && Date.today >= block_changes_at
end
def cloud_licensing?
cloud_licensing_enabled == true
end
def offline_cloud_licensing?
offline_cloud_licensing_enabled == true
end
def auto_renew?
auto_renew_enabled == true
end
def seat_reconciliation?
seat_reconciliation_enabled == true
end
def operational_metrics?
operational_metrics_enabled == true
end
def generated_from_customers_dot?
generated_from_customers_dot == true
end
def restricted?(key = nil)
if key
restricted? && restrictions.has_key?(key)
else
restrictions && restrictions.length >= 1
end
end
def attributes
hash = {}
hash['version'] = version
hash['licensee'] = licensee
hash['issued_at'] = starts_at
hash['expires_at'] = expires_at if will_expire?
hash['notify_admins_at'] = notify_admins_at if will_notify_admins?
hash['notify_users_at'] = notify_users_at if will_notify_users?
hash['block_changes_at'] = block_changes_at if will_block_changes?
hash['next_sync_at'] = next_sync_at if will_sync?
hash['last_synced_at'] = last_synced_at if will_sync?
hash['activated_at'] = activated_at if activated?
hash['cloud_licensing_enabled'] = cloud_licensing?
hash['offline_cloud_licensing_enabled'] = offline_cloud_licensing?
hash['auto_renew_enabled'] = auto_renew?
hash['seat_reconciliation_enabled'] = seat_reconciliation?
hash['operational_metrics_enabled'] = operational_metrics?
hash['generated_from_customers_dot'] = generated_from_customers_dot?
hash['restrictions'] = restrictions if restricted?
hash
end
def to_json(*_args)
JSON.dump(attributes)
end
def export(boundary: nil)
validate!
puts to_json
data = self.class.encryptor.encrypt(to_json)
data = Boundary.add_boundary(data, boundary) if boundary
data
end
private
def load_attributes(attributes)
attributes = attributes.transform_keys(&:to_s)
version = attributes['version'] || 1
raise ArgumentError, 'Version is too new' unless version && version == 1
@version = version
@licensee = attributes['licensee']
%w[issued_at expires_at notify_admins_at notify_users_at block_changes_at].each do |attr_name|
set_date_attribute(attr_name, attributes[attr_name])
end
%w[last_synced_at next_sync_at activated_at].each do |attr_name|
set_datetime_attribute(attr_name, attributes[attr_name])
end
%w[
cloud_licensing_enabled
offline_cloud_licensing_enabled
auto_renew_enabled
seat_reconciliation_enabled
operational_metrics_enabled
generated_from_customers_dot
].each do |attr_name|
public_send("#{attr_name}=", attributes[attr_name] == true)
end
restrictions = attributes['restrictions']
if restrictions&.is_a?(Hash)
restrictions = restrictions.transform_keys(&:to_sym)
@restrictions = restrictions
end
end
def set_date_attribute(attr_name, value, date_class = Date)
value = date_class.parse(value) rescue nil if value.is_a?(String)
return unless value
public_send("#{attr_name}=", value)
end
def set_datetime_attribute(attr_name, value)
set_date_attribute(attr_name, value, DateTime)
end
end
end
# MARK: GENERATOR
if !File.file?("license_key") || !File.file?("license_key.pub")
puts "License key not found"
puts "Generate a RSA key pair using generate_keys.rb"
exit
end
public_key = OpenSSL::PKey::RSA.new File.read("license_key.pub")
private_key = OpenSSL::PKey::RSA.new File.read("license_key")
Gitlab::License.encryption_key = private_key
license = Gitlab::License.new
license.licensee = {
"Name" => "GitLab Inc.",
"Company" => "GitLab Inc.",
"Email" => "support@gitlab.com"
}
license.starts_at = Date.new(2000, 1, 1)
license.restrictions = {
plan: 'ultimate',
active_user_count: 100000000,
}
data = license.export
File.open("result.gitlab-license", "w") { |f| f.write(data) }

27
license_key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAud4kAhXh7dQg9qlhqbwUd1wrknf06vrT2Wc72lhag5jMtWSs
HWEC817zNOTNkev3q6RYXUO0cm+fcZsO4g3Iy4ZbM9s5kXY1jDaM4Mc6JCehO7o1
1f4mY6FY6D1RHpk7GCKkS7ApbIjYkNt7TRYVNUeMoY82g14NWjehWroOi23FOn4U
XPX20mBCRjK0RFLQ26j0rWxHpD3yz0cbY8qx3b9v/Jazlk282IakorxaHVTYN5ap
lspif3M1Ku36mRVXUVkoYRJl8vjUgicut6vcqzrTk14l3quyPKri4Tnps/ZLvWGp
d53UvEEevZxJVKVxdtgH0O2UCRVRXSuiG74ZCwIDAQABAoIBAEDlIKlhvoptQD0f
EqxSsMqj8cqn+2l3vjPv6WPo6WF9HixPRBDV6FPU2RGkuWmze7wAG6Ikm4JBGuht
fRrMOUlmVb2bU1RIc5XLDhEFPnWVKKRT9awLmpe6o/IiRopqccmRfs+2aCAu/35E
Q568kRcTLjTSbfQcCIlxVvL4d0+SoYbYnuUWZ5oM20FC/HcLjWRlEW8UAQ0guKgw
MRw4Yw1vOOZqzsrQThWcam7DgyhOs3oCAbV3j4ZaoZpmDgLRt1Kvx2ZGUOoqh/ly
rONuah1ESGrLmNy4NLNlXI6IArisoZQj2uosC7nbIlmLkuRhFNLPjx/nw66FGw1A
8pP56EECgYEA5JLKSJy9RH6nS52h0UmnrhmkwJloLm/s4bVgE6xD8V2O9Lk+nUYz
8MEpGUgpN1WenjTz7EruANZtIP9qx7mPptEk34D+JBEprS/gMaaKb9hKJ46kNTqp
zrN7CUsk5nHGaKCChJA4JCUm2LCNna3gB2Bf3bcliXvJQb9Sa7Kk61ECgYEA0CuJ
wUKLkX8xfJNUKYHFx8GysNfgYqxjFOUWHcwM7LHWgVS1+/sgym+4qFbn35aIHN6G
87hHddgT+XyVVz8GTZ5DykA1SvvLJZOKM4cxP/EkkI0sSJLPmETC7Py2gZs0Z3qF
KOipcchosrMbpLzmprRkfzlGwl1nhU7/JCRD75sCgYA+OHc4LPKYoqGHw/E4t4Qd
sH1YsGnbujwRdP4iXNJh8cXoeETDK0kYUHyPlUUi+vuitWdw+zSupbAvO1gl5i1k
i6ot7T9BMirWKiItYdhtecM14W5xzvZKfjEP5pS05mPMN2VQELI3pKVedzEVqy9A
0stF34UoV7oBW8Nj7c1XAQKBgDyCi1Zb64nteQsHIE24ZS89hJ2XAqhsB5kJRjZ/
G7qprvqFDykhxFRTyU9Vg60gaoxJutyZUlxU5Ol+Z0KnFUP2nynpJBSZwGE509BK
mexGQiSqhJbL5gAS7L5KbxqZbNAvcwmDJ83lPVnEamKmbj1C7nt0wLa6w96iKdPt
nrnFAoGBAKTcZDk/YecmnvRYNB5su5bQLjQZZhdGECvEvVBMyqm87pFUwjuVIkG7
pYs2xLsC58kieI7fbWQ+ZG8ZTIs+bdOkJpGFYOekZAYeTJPep/RIVpskcpfPhEit
AG0SbqXcVZTzY+kRTSttBijUbvYGsHccUSJnvJY65FQC6OZJCDkz
-----END RSA PRIVATE KEY-----

9
license_key.pub Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAud4kAhXh7dQg9qlhqbwU
d1wrknf06vrT2Wc72lhag5jMtWSsHWEC817zNOTNkev3q6RYXUO0cm+fcZsO4g3I
y4ZbM9s5kXY1jDaM4Mc6JCehO7o11f4mY6FY6D1RHpk7GCKkS7ApbIjYkNt7TRYV
NUeMoY82g14NWjehWroOi23FOn4UXPX20mBCRjK0RFLQ26j0rWxHpD3yz0cbY8qx
3b9v/Jazlk282IakorxaHVTYN5aplspif3M1Ku36mRVXUVkoYRJl8vjUgicut6vc
qzrTk14l3quyPKri4Tnps/ZLvWGpd53UvEEevZxJVKVxdtgH0O2UCRVRXSuiG74Z
CwIDAQAB
-----END PUBLIC KEY-----

10
make.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
ruby ./generate_keys.rb
ruby ./generate_licenses.rb
echo "done"

22
result.gitlab-license Normal file
View File

@ -0,0 +1,22 @@
eyJkYXRhIjoieThyOWZHMjQyMGlMcEFnaS9nOFYxZmNobitoakxWSmszYTRU
czhTdTZ0VG80akFLdXhNM1NQbGJMNi92XG4yRFNQcXJBQWltRE1vVnNmSkJu
ZG9uUVA5WHByRlJ1NlhHQndBK3p3eDB2b01hNURLNlVaS3hYUDN5QnFcbnda
VzdLanpUK0lYNU5nNmlGSktLWFJVdy9nNnRYeHFmdEVtc2laK0RlR3FNbzl3
M0VBT0t4cHgyN2Z0clxuQ2VHckZqQTNaVGpVRkdkMWNwTlE5dHJjR21oQnhI
TXdmQlp5OUFVZFNWR2RkeC9HMU85N24xNC9sRG56XG5lVTJHUmRvcUtUbmtv
bnJkQzlFOTBuY21ERkFlaTQ1dGlrM2V4enFlMjBEN3hFbFQ5bm80N3lXT2VR
QU9cbitLMTBwZlMzWnoxNTBNSk9XZTBxUVdkeE8wQ0FwNVI3NFJmUjQ2Zlg0
Tkd4WDlCSGhuK1U4TWZ3aDRhWlxuMU9ZT2FxemFwUDFiQmZNNnNHenV6bHJT
SnN0Um8yUHFEYVp6WlJjZEtSQnU0elRPV1FxSzBzVmlzWFhvXG55TDJxM3l1
cmFWaFA1UW5DK2ljL0VDcGJ0M2w3dWtNR095UCtDc0hPRWsveE5jekdXQWNo
V2xmYzVaNTZcbld6UnFpcnkvR1dWR1ZJNXpMMUt0ekFVS2FlMkZtYXFxSFoy
WjlKK3JSdGxXMkR4T3NCOWpidz09XG4iLCJrZXkiOiJZby9MK0xBQy9tRWZz
MTVnTVI4TTNLQ29zdDQzTkVJY01zQXlKY1JVeGs4aG1mV2xLZmpXWkhBaG9D
TUlcbkZzanloalpWeUsvMkVHOFNML05uQmQzckErYjl5T3RlbEpFOTMzQko1
aFZWcTdrZFRBNE5CTTlGYzArWlxueFJtTDUzcXNiQlA1MUtDL2t4K2t1ZDRJ
ZXNFalFiRk5NQVpyWGE5Rkd6VmVySUR2U2phQzZPc3BwQ0lVXG5PTjR0NEhP
eTQ5dUszbUdPRDlvMGRZTUNCd25qMW50RzZhUlNhRjBhZXlQbS9rZy9ZcHhv
QTExZ2k3UWtcbmtHbCtuSzBnZEtkMGNnemk1TzlUcDBCUHVEWTVZd2wwcFlW
WGJSNU4yWE90WlBnWFJkSmJqMjVMK1F5VlxuNmZGcnBHZmxzdllaWjB3OEFH
TnBlQU5WS3l4bytBVVVEcTlMNlgzdU5BPT1cbiIsIml2IjoielJPZGwxbTcr
b1NKVmptYmFDaWUwQT09XG4ifQ==