diff --git a/feature.scan.py b/feature.scan.py deleted file mode 100755 index b0fcc87..000000000 --- a/feature.scan.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import re -import os -import sys - -SCANNING_DIR = sys.argv[1] -OUTPUT_LIST_FILE = sys.argv[2] -print(f"[*] scanning directory: {SCANNING_DIR}") - -# use regex to find the string -# eg ::License.feature_available?(:aaa) || ::Feature.enabled?(:bbb, self) -# make sure +? for shortest match -REGEX_PARTTERN = "License.feature_available\?\(:.+?\)" - -REQUIRED_FILE_SUFFIX = ['rb'] - -scanning_file_list = set() -def build_file_list(input: str): - global scanning_file_list - scanning_file_list.add(input) - if os.path.isdir(input): - for file in os.listdir(input): - build_file_list(os.path.join(input, file)) -build_file_list(SCANNING_DIR) - -print(f"[*] scanning {len(scanning_file_list)} files...") -feature_list=set() -for file in scanning_file_list: - if not os.path.isfile(file): continue - if not file.split(".")[-1] in REQUIRED_FILE_SUFFIX: continue - with open(file, "r") as f: - content = f.read() - all_match = re.findall(REGEX_PARTTERN, content) - all_match = [x.split(":")[1].split(")")[0] for x in all_match] - all_match = [x for x in all_match if x] - feature_list.update(all_match) -print(f"[*] found {len(feature_list)} features") - -feature_list = list(feature_list) -feature_list.sort() - -print(f"[*] writing to {OUTPUT_LIST_FILE}...") -with open(OUTPUT_LIST_FILE, "w") as f: - for feature in feature_list: - f.write(feature + "\n") - -print(f"[*] done") diff --git a/generate_licenses.rb b/generate_licenses.rb deleted file mode 100644 index 344b4bc..000000000 --- a/generate_licenses.rb +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -require 'openssl' -require_relative 'lib/license.rb' -puts "[i] lib gitlab-license: #{Gitlab::License::VERSION}" - -OUTPUT_DIR = ARGV[0] -puts "[*] output dir: #{OUTPUT_DIR}" - -LICENSE_TARGET_PRIVATE_KEY = "license_key" -LICENSE_TARGET_PUBLIC_KEY = "license_key.pub" -TARGET_LICENSE_FILE = 'result.gitlab-license' -TARGET_PLAIN_LICENSE_FILE = 'result.gitlab-license.json' - -FEATURE_LIST = [] -if ARGV[1].nil? - puts "[i] you can provide a list of feature to be enabled inside license" -else - FEATURE_LIST_FILE = ARGV[1] - File.open(FEATURE_LIST_FILE).each do |line| - FEATURE_LIST.push(line.chomp) - end - FEATURE_LIST.uniq! -end -puts "[*] loaded #{FEATURE_LIST.length} features" - -Dir.chdir(OUTPUT_DIR) -puts "[*] switching working dir: #{Dir.pwd}" - -puts "[*] generating license..." -if !File.exist?(LICENSE_TARGET_PRIVATE_KEY) || !File.exist?(LICENSE_TARGET_PUBLIC_KEY) - puts "[*] generating rsa key pair..." - key = OpenSSL::PKey::RSA.new(2048) - File.write(LICENSE_TARGET_PRIVATE_KEY, key.to_pem) - File.write(LICENSE_TARGET_PUBLIC_KEY, key.public_key.to_pem) -end - -puts "[*] loading key pair..." - -public_key = OpenSSL::PKey::RSA.new File.read(LICENSE_TARGET_PUBLIC_KEY) -private_key = OpenSSL::PKey::RSA.new File.read(LICENSE_TARGET_PRIVATE_KEY) - -puts "[*] building license..." - -Gitlab::License.encryption_key = private_key - -license = Gitlab::License.new - -# don't use gitlab inc, search `gl_team_license` in lib for details -license.licensee = { - "Name" => "Tim Cook", - "Company" => "Apple Computer, Inc.", - "Email" => "tcook@apple.com" -} - -# required of course -license.starts_at = Date.new(1976, 4, 1) - -# required since gem gitlab-license v2.2.1 -license.expires_at = Date.new(2500, 4, 1) - -# prevent gitlab crash at -# notification_start_date = trial? ? expires_at - NOTIFICATION_DAYS_BEFORE_TRIAL_EXPIRY : block_changes_at -license.block_changes_at = Date.new(2500, 4, 1) - -# required -license.restrictions = { - plan: 'ultimate', - # STARTER_PLAN = 'starter' - # PREMIUM_PLAN = 'premium' - # ULTIMATE_PLAN = 'ultimate' - - active_user_count: 2147483647, - # required, just dont overflow -} - -if !license.valid? - puts "[E] license validation failed!" - puts "[E] #{license.errors}" - exit 1 -end - -puts "[*] exporting license file..." - -File.open(TARGET_PLAIN_LICENSE_FILE, "w") { |f| f.write(JSON.pretty_generate(JSON.parse(license.to_json))) } - -data = license.export -File.open(TARGET_LICENSE_FILE, "w") { |f| f.write(data) } - -puts "[*] done" diff --git a/make.sh b/make.sh index 2f7649e..22d8ce8 100755 --- a/make.sh +++ b/make.sh @@ -9,7 +9,7 @@ if [ ! -f ".root" ]; then fi WORKING_DIR=$(pwd) -mkdir temp || true +mkdir temp 2> /dev/null || true echo "[*] fetching ruby gem version..." RB_GEM_NAME="gitlab-license" @@ -28,7 +28,7 @@ echo "[*] gitlab-license version: $RB_GEM_VERSION" RB_GEM_DOWNLOAD_URL="https://rubygems.org/downloads/gitlab-license-$RB_GEM_VERSION.gem" RB_GEM_DOWNLOAD_PATH=$(pwd)/temp/gem/gitlab-license.gem mkdir -p $(dirname $RB_GEM_DOWNLOAD_PATH) -curl -L $RB_GEM_DOWNLOAD_URL -o $RB_GEM_DOWNLOAD_PATH +curl -L $RB_GEM_DOWNLOAD_URL -o $RB_GEM_DOWNLOAD_PATH 1> /dev/null 2> /dev/null pushd $(dirname $RB_GEM_DOWNLOAD_PATH) > /dev/null tar -xzf gitlab-license.gem tar -xzf data.tar.gz @@ -63,19 +63,38 @@ fi echo "[*] updating gitlab source code..." pushd $GITLAB_SOURCE_CODE_DIR > /dev/null -git clean -fdx -f -git reset --hard -git pull +git clean -fdx -f > /dev/null +git reset --hard > /dev/null +git pull > /dev/null popd > /dev/null +BUILD_DIR=$(pwd)/build +mkdir -p $BUILD_DIR + echo "[*] scanning features..." -FEATURE_LIST_FILE=$(pwd)/temp/features.txt +FEATURE_LIST_FILE=$BUILD_DIR/features.json rm -f $FEATURE_LIST_FILE || true -./feature.scan.py $GITLAB_SOURCE_CODE_DIR $FEATURE_LIST_FILE +./src/scan.features.rb \ + -o $FEATURE_LIST_FILE \ + -s $GITLAB_SOURCE_CODE_DIR + +echo "[*] generating key pair..." +PUBLIC_KEY_FILE=$BUILD_DIR/public.key +PRIVATE_KEY_FILE=$BUILD_DIR/private.key +./src/generator.keys.rb \ + --public-key $PUBLIC_KEY_FILE \ + --private-key $PRIVATE_KEY_FILE \ + || true # ignore error if key already exists echo "[*] generating license..." -OUTPUT_DIR=$(pwd)/output -mkdir -p $OUTPUT_DIR -ruby ./generate_licenses.rb $OUTPUT_DIR $FEATURE_LIST_FILE +LICENSE_FILE=$BUILD_DIR/license.data +LICENSE_JSON_FILE=$BUILD_DIR/license.json + +./src/generator.license.rb \ + -f $FEATURE_LIST_FILE \ + --public-key $PUBLIC_KEY_FILE \ + --private-key $PRIVATE_KEY_FILE \ + -o $LICENSE_FILE \ + --plain-license $LICENSE_JSON_FILE echo "[*] done $(basename $0)" \ No newline at end of file diff --git a/src/generator.keys.rb b/src/generator.keys.rb new file mode 100755 index 000000000..c52370b --- /dev/null +++ b/src/generator.keys.rb @@ -0,0 +1,44 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +require 'optparse' +require 'openssl' + +public_key_file = nil +private_key_file = nil + +OptionParser.new do |opts| + opts.banner = "Usage: generator.keys.rb [options]" + + opts.on("--public-key PATH", "Specify public key file (required)") do |v| + public_key_file = File.expand_path(v) + end + + opts.on("--private-key PATH", "Specify private key file (required)") do |v| + private_key_file = File.expand_path(v) + end + + opts.on("-h", "--help", "Prints this help") do + puts opts + exit + end +end.parse! + +if public_key_file.nil? || private_key_file.nil? + puts "[!] missing required options" + puts "[!] use -h for help" + exit 1 +end + +if File.exist?(private_key_file) || File.exist?(public_key_file) + puts "[!] key pair already exists" + puts "[!] remove them if you want to regenerate" + exit 1 +end + +puts "[*] generating rsa key pair..." +key = OpenSSL::PKey::RSA.new(2048) +File.write(private_key_file, key.to_pem) +File.write(public_key_file, key.public_key.to_pem) + +puts "[*] done" diff --git a/src/generator.license.rb b/src/generator.license.rb new file mode 100755 index 000000000..6d6d61d --- /dev/null +++ b/src/generator.license.rb @@ -0,0 +1,127 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +license_file_path = nil +license_json_path = nil +public_key_path = nil +private_key_path = nil +features_json_path = nil + +require 'optparse' +OptionParser.new do |opts| + opts.banner = "Usage: generator.license.rb [options]" + + opts.on("-o", "--output PATH", "Output to dir (required)") do |v| + license_file_path = File.expand_path(v) + end + + opts.on("--public-key PATH", "Specify public key file (required)") do |v| + public_key_path = File.expand_path(v) + end + + opts.on("--private-key PATH", "Specify private key file (required)") do |v| + private_key_path = File.expand_path(v) + end + + opts.on("-f", "--features PATH", "Specify features json file (optional)") do |v| + features_json_path = File.expand_path(v) + end + + opts.on("--plain-license PATH", "Export license in json if set, useful for debug. (optional)") do |v| + license_json_path = File.expand_path(v) + end + + opts.on("-h", "--help", "Prints this help") do + puts opts + exit + end +end +.parse! + +if license_file_path.nil? || public_key_path.nil? || private_key_path.nil? + puts "[!] missing required options" + puts "[!] use -h for help" + exit 1 +end + +# ========== + +puts "[*] loading keys..." +require 'openssl' +PUBLIC_KEY = OpenSSL::PKey::RSA.new File.read(public_key_path) +PRIVATE_KEY = OpenSSL::PKey::RSA.new File.read(private_key_path) + +puts "[*] loading licenses..." +require_relative '../lib/license.rb' +puts "[i] lib gitlab-license: #{Gitlab::License::VERSION}" + +if !features_json_path.nil? + puts "[*] loading features from #{features_json_path}" + require 'json' + FEATURE_LIST = JSON.parse(File.read(features_json_path)) +else + FEATURE_LIST = [] +end +puts "[*] total features to inject: #{FEATURE_LIST.size}" + +# ========== + +puts "[*] building a license..." + +Gitlab::License.encryption_key = PRIVATE_KEY + +license = Gitlab::License.new + +# don't use gitlab inc, search `gl_team_license` in lib for details +license.licensee = { + "Name" => "Tim Cook", + "Company" => "Apple Computer, Inc.", + "Email" => "tcook@apple.com" +} + +# required of course +license.starts_at = Date.new(1976, 4, 1) + +# required since gem gitlab-license v2.2.1 +license.expires_at = Date.new(2500, 4, 1) + +# prevent gitlab crash at +# notification_start_date = trial? ? expires_at - NOTIFICATION_DAYS_BEFORE_TRIAL_EXPIRY : block_changes_at +license.block_changes_at = Date.new(2500, 4, 1) + +# required +license.restrictions = { + plan: 'ultimate', + # STARTER_PLAN = 'starter' + # PREMIUM_PLAN = 'premium' + # ULTIMATE_PLAN = 'ultimate' + + active_user_count: 2147483647, + # required, just dont overflow +} + +# restricted_attr will access restrictions +# add_ons will access restricted_attr(:add_ons, {}) +# so here by we inject all features into restrictions +# see scan.rb for a list of features that we are going to inject +for feature in FEATURE_LIST + license.restrictions[feature] = 2147483647 +end + +if !license.valid? + puts "[E] license validation failed!" + puts "[E] #{license.errors}" + exit 1 +end + +puts "[*] exporting license file..." + +if !license_json_path.nil? + puts "[*] writing to #{license_json_path}" + File.write(license_json_path, JSON.pretty_generate(JSON.parse(license.to_json))) +end + +puts "[*] writing to #{license_file_path}" +File.write(license_file_path, license.export) + +puts "[*] done"