gitlab-license 0.0.1

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
+ SHA1:
3
+ metadata.gz: 003f891e0c79c5dad69dd13991814fd337dd842f
4
+ data.tar.gz: 9a324afa2d6c474d654baddded5d8c8f06c06e2c
5
+ SHA512:
6
+ metadata.gz: 902fe5b33b61f2c259a638c4ed9bfdb05cade11046e4c7913557df9030e547ea29e8fa3e363208fd0d4f13b460b3300d646a78ef3d133444106aee2209d9850f
7
+ data.tar.gz: 0ca91a33ad564337b64b691ddd7badb405899bc3a7b1d6d8c8e45e08397e59b4a031d30836553ea7568f9ba49ae7c7d49af86063f08e3379b58cb152f9e18161
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gitlab-license.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 GitLab B.V.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,162 @@
1
+ # Gitlab::License
2
+
3
+ gitlab-license helps you generate, verify and enforce software licenses. It is used in GitLab Enterprise Edition.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'gitlab-license'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install gitlab-license
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ # Generate a key pair. You should do this only once.
25
+ key_pair = OpenSSL::PKey::RSA.generate(2048)
26
+
27
+ # Write it to a file to use in the license generation application.
28
+ File.open("license_key", "w") { |f| f.write(key_pair.to_pem) }
29
+
30
+ # Extract the public key.
31
+ public_key = key_pair.public_key
32
+ # Write it to a file to ship along with the main application.
33
+ File.open("license_key.pub", "w") { |f| f.write(public_key.to_pem) }
34
+
35
+ # In the license generation application, load the private key from a file.
36
+ private_key = OpenSSL::PKey::RSA.new File.read("license_key")
37
+ Gitlab::License.encryption_key = private_key
38
+
39
+ # Build a new license.
40
+ license = Gitlab::License.new
41
+
42
+ # License information to be rendered as a table in the admin panel.
43
+ # E.g.: "This instance of GitLab Enterprise Edition is licensed to:"
44
+ # Specific keys don't matter, but there needs to be at least one.
45
+ license.licensee = {
46
+ "Name" => "Douwe Maan",
47
+ "Company" => "GitLab B.V.",
48
+ "Email" => "douwe@gitlab.com"
49
+ }
50
+
51
+ # The date the license was issued.
52
+ # Required.
53
+ license.issued_at = Date.new(2015, 4, 24)
54
+ # The date the license expires.
55
+ # Not required, to allow lifetime licenses.
56
+ license.expires_at = Date.new(2016, 4, 23)
57
+
58
+ # The below dates are hardcoded in the license so that you can play with the
59
+ # period after which there are "repercussions" to license expiration.
60
+
61
+ # The date admins will be notified about the license's pending expiration.
62
+ # Not required.
63
+ license.notify_admins_at = Date.new(2016, 4, 19)
64
+
65
+ # The date regular users will be notified about the license's pending expiration.
66
+ # Not required.
67
+ license.notify_users_at = Date.new(2016, 4, 23)
68
+
69
+ # The date "changes" like code pushes, issue or merge request creation
70
+ # or modification and project creation will be blocked.
71
+ # Not required.
72
+ license.block_changes_at = Date.new(2016, 5, 7)
73
+
74
+ # Restrictions bundled with this license.
75
+ # Not required, to allow unlimited-user licenses for things like educational organizations.
76
+ license.restrictions = {
77
+ # The maximum allowed number of active users.
78
+ # Not required.
79
+ active_user_count: 10000
80
+
81
+ # We don't currently have any other restrictions, but we might in the future.
82
+ }
83
+
84
+ puts "License:"
85
+ puts license
86
+
87
+ # Export the license, which encrypts and encodes it.
88
+ data = license.export
89
+
90
+ puts "Exported license:"
91
+ puts data
92
+
93
+ # Write the license to a file to send to a customer.
94
+ File.open("GitLabBV.gitlab-license", "w") { |f| f.write(data) }
95
+
96
+
97
+ # In the customer's application, load the public key from a file.
98
+ public_key = OpenSSL::PKey::RSA.new File.read("license_key.pub")
99
+ Gitlab::License.encryption_key = public_key
100
+
101
+ # Read the license from a file.
102
+ data = File.read("GitLabBV.gitlab-license")
103
+
104
+ # Import the license, which decodes and decrypts it.
105
+ $license = Gitlab::License.import(data)
106
+
107
+ puts "Imported license:"
108
+ puts $license
109
+
110
+ # Quit if the license is invalid
111
+ unless $license
112
+ raise "The license is invalid."
113
+ end
114
+
115
+ # Quit if the active user count exceeds the allowed amount:
116
+ if $license.restricted?(:active_user_count)
117
+ active_user_count = User.active.count
118
+ if active_user_count > $license.restrictions[:active_user_count]
119
+ raise "The active user count exceeds the allowed amount!"
120
+ end
121
+ end
122
+
123
+ # Show admins a message if the license is about to expire.
124
+ if $license.notify_admins?
125
+ puts "The license is due to expire on #{$license.expires_at}."
126
+ end
127
+
128
+ # Show users a message if the license is about to expire.
129
+ if $license.notify_users?
130
+ puts "The license is due to expire on #{$license.expires_at}."
131
+ end
132
+
133
+ # Block pushes when the license expired two weeks ago.
134
+ module Gitlab
135
+ class GitAccess
136
+ # ...
137
+ def check(cmd, changes = nil)
138
+ if $license.block_changes?
139
+ return build_status_object(false, "License expired")
140
+ end
141
+
142
+ # Do other Git access verification
143
+ # ...
144
+ end
145
+ # ...
146
+ end
147
+ end
148
+
149
+ # Show information about the license in the admin panel.
150
+ puts "This instance of GitLab Enterprise Edition is licensed to:"
151
+ $license.licensee.each do |key, value|
152
+ puts "#{key}: #{value}"
153
+ end
154
+
155
+ if $license.expired?
156
+ puts "The license expired on #{$license.expires_at}"
157
+ elsif $license.will_expire?
158
+ puts "The license will expire on #{$license.expires_at}"
159
+ else
160
+ puts "The license will never expire."
161
+ end
162
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gitlab/license"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gitlab/license/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gitlab-license"
8
+ spec.version = Gitlab::License::VERSION
9
+ spec.authors = ["Douwe Maan"]
10
+ spec.email = ["douwe@gitlab.com"]
11
+
12
+ spec.summary = %q{gitlab-license helps you generate, verify and enforce software licenses.}
13
+ spec.homepage = "https://gitlab.com/gitlab-org/gitlab-license"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.9"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "byebug"
24
+ end
@@ -0,0 +1,155 @@
1
+ require "openssl"
2
+ require "date"
3
+ require "json"
4
+ require "base64"
5
+
6
+ require "gitlab/license/version"
7
+ require "gitlab/license/encryptor"
8
+
9
+ module Gitlab
10
+ class License
11
+ class << self
12
+ attr_accessor :encryption_key
13
+ @encryption_key = nil
14
+
15
+ def encryptor
16
+ unless self.encryption_key && self.encryption_key.is_a?(OpenSSL::PKey::RSA)
17
+ raise "No RSA encryption key provided."
18
+ end
19
+
20
+ Encryptor.new(self.encryption_key)
21
+ end
22
+
23
+ def import(data)
24
+ from_json(encryptor.decrypt(data))
25
+ end
26
+
27
+ def from_json(license_json)
28
+ new(JSON.parse(license_json))
29
+ end
30
+ end
31
+
32
+ attr_reader :version
33
+ attr_accessor :licensee, :issued_at, :expires_at
34
+ attr_accessor :notify_admins_at, :notify_users_at, :block_changes_at
35
+ attr_accessor :restrictions
36
+
37
+ def initialize(attributes = {})
38
+ load_attributes(attributes)
39
+ end
40
+
41
+ def valid?
42
+ return false if !licensee || !licensee.is_a?(Hash) || licensee.length == 0
43
+ return false if !issued_at || !issued_at.is_a?(Date)
44
+ return false if expires_at && !expires_at.is_a?(Date)
45
+ return false if notify_admins_at && !notify_admins_at.is_a?(Date)
46
+ return false if notify_users_at && !notify_users_at.is_a?(Date)
47
+ return false if block_changes_at && !block_changes_at.is_a?(Date)
48
+ return false if restrictions && !restrictions.is_a?(Hash)
49
+
50
+ true
51
+ end
52
+
53
+ def validate!
54
+ raise "License is invalid" unless valid?
55
+ end
56
+
57
+ def will_expire?
58
+ self.expires_at
59
+ end
60
+
61
+ def will_notify_admins?
62
+ self.notify_admins_at
63
+ end
64
+
65
+ def will_notify_users?
66
+ self.notify_users_at
67
+ end
68
+
69
+ def will_block_changes?
70
+ self.block_changes_at
71
+ end
72
+
73
+ def expired?
74
+ will_expire? && Date.today >= self.expires_at
75
+ end
76
+
77
+ def notify_admins?
78
+ will_notify_admins? && Date.today >= self.notify_admins_at
79
+ end
80
+
81
+ def notify_users?
82
+ will_notify_users? && Date.today >= self.notify_users_at
83
+ end
84
+
85
+ def block_changes?
86
+ will_block_changes? && Date.today >= self.block_changes_at
87
+ end
88
+
89
+ def restricted?(key = nil)
90
+ if key
91
+ restricted? && restrictions.has_key?(key)
92
+ else
93
+ restrictions && restrictions.length >= 1
94
+ end
95
+ end
96
+
97
+ def attributes
98
+ hash = {}
99
+
100
+ hash["version"] = self.version
101
+ hash["licensee"] = self.licensee
102
+
103
+ hash["issued_at"] = self.issued_at
104
+ hash["expires_at"] = self.expires_at if self.will_expire?
105
+
106
+ hash["notify_admins_at"] = self.notify_admins_at if self.will_notify_admins?
107
+ hash["notify_users_at"] = self.notify_users_at if self.will_notify_users?
108
+ hash["block_changes_at"] = self.block_changes_at if self.will_block_changes?
109
+
110
+ hash["restrictions"] = self.restrictions if self.restricted?
111
+
112
+ hash
113
+ end
114
+
115
+ def to_json
116
+ JSON.dump(self.attributes)
117
+ end
118
+
119
+ def export
120
+ validate!
121
+
122
+ self.class.encryptor.encrypt(self.to_json)
123
+ end
124
+
125
+ private
126
+
127
+ def load_attributes(attributes)
128
+ attributes = Hash[attributes.map { |k, v| [k.to_s, v] }]
129
+
130
+ version = attributes["version"] || 1
131
+ unless version && version == 1
132
+ raise ArgumentError, "Version is too new"
133
+ end
134
+
135
+ @version = version
136
+
137
+ @licensee = attributes["licensee"]
138
+
139
+ %w(issued_at expires_at notify_admins_at notify_users_at block_changes_at).each do |attr|
140
+ value = attributes[attr]
141
+ value = Date.parse(value) rescue nil if value.is_a?(String)
142
+
143
+ next unless value
144
+
145
+ send("#{attr}=", value)
146
+ end
147
+
148
+ restrictions = attributes["restrictions"]
149
+ if restrictions && restrictions.is_a?(Hash)
150
+ restrictions = Hash[restrictions.map { |k, v| [k.to_sym, v] }]
151
+ @restrictions = restrictions
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,63 @@
1
+ module Gitlab
2
+ class License
3
+ class Encryptor
4
+ attr_accessor :key
5
+
6
+ def initialize(key)
7
+ @key = key
8
+ end
9
+
10
+ def encrypt(data)
11
+ unless key.private?
12
+ raise "Provided key is not a private key."
13
+ end
14
+
15
+ # Encrypt the data using symmetric AES encryption.
16
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
17
+ cipher.encrypt
18
+ aes_key = cipher.random_key
19
+ aes_iv = cipher.random_iv
20
+
21
+ encrypted_data = cipher.update(data) + cipher.final
22
+
23
+ # Encrypt the AES key using asymmetric RSA encryption.
24
+ encrypted_key = self.key.private_encrypt(aes_key)
25
+
26
+ encryption_data = {
27
+ "data" => Base64.encode64(encrypted_data),
28
+ "key" => Base64.encode64(encrypted_key),
29
+ "iv" => Base64.encode64(aes_iv)
30
+ }
31
+
32
+ json_data = JSON.dump(encryption_data)
33
+ Base64.encode64(json_data)
34
+ end
35
+
36
+ def decrypt(data)
37
+ unless key.public?
38
+ raise "Provided key is not a public key."
39
+ end
40
+
41
+ json_data = Base64.decode64(data)
42
+ encryption_data = JSON.parse(json_data)
43
+
44
+ encrypted_data = Base64.decode64(encryption_data["data"])
45
+ encrypted_key = Base64.decode64(encryption_data["key"])
46
+ aes_iv = Base64.decode64(encryption_data["iv"])
47
+
48
+ # Decrypt the AES key using asymmetric RSA encryption.
49
+ aes_key = self.key.public_decrypt(encrypted_key)
50
+
51
+ # Decrypt the data using symmetric AES encryption.
52
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
53
+ cipher.decrypt
54
+ cipher.key = aes_key
55
+ cipher.iv = aes_iv
56
+
57
+ data = cipher.update(encrypted_data) + cipher.final
58
+
59
+ data
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ module Gitlab
2
+ class License
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab-license
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Douwe Maan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-04-29 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
+ - douwe@gitlab.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - gitlab-license.gemspec
72
+ - lib/gitlab/license.rb
73
+ - lib/gitlab/license/encryptor.rb
74
+ - lib/gitlab/license/version.rb
75
+ homepage: https://gitlab.com/gitlab-org/gitlab-license
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: gitlab-license helps you generate, verify and enforce software licenses.
99
+ test_files: []