Merge branch 'license' into 'master'

Allow license to be uploaded and enforce its validity.

Addresses #274.

Still a work in progress, there are no tests yet, some things need extra validation or prettier error messages, and we're not doing anything to enforce active user count yet. Still, the flow stands! Let me know what you think :)

- [x] Check active user count at moment of upload
- [x] Prettier validation / error message when license is invalid
- [x] Prettier error when public key is invalid
- [x] Tests
- [x] Work on copy

These will go into separate MRs as they require more discussion:

- Check historic active user count over past year
- Add application setting to disallow creating/activating new user when limit is reached

-----

No license:

![no_license](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/41168c7141b3c121f2d3137b8566f467/no_license.png)

Also shown when pushing code:

![no_license_shell](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/bc11926a59b266d28ffd61b3de48d54b/no_license_shell.png)

The "Upload a new license in the admin area" part is only for admins, regular users will see "Ask an admin to upload a new license".

Upload page in admin area:

![upload_license](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/44d28581dd0629e67fb014a817ca5675/upload_license.png)

License details in admin area:

![license](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/8836f2b6e0bf8db28673d758eb57338b/license.png)

Notice of a license that will expire: Also notice the license history at the bottom.

![license_will_expire](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/5396b6b3e46f5bb53d5ce33cf5bd23ea/license_will_expire.png)

Notice of an expired license:

![license_expired](https://dev.gitlab.org/gitlab/gitlab-ee/uploads/d4b582a212738054f52ed1909e45568d/license_expired.png)

cc @dzaporozhets @sytse

See merge request !383
This commit is contained in:
Douwe Maan
2015-05-07 18:54:47 +00:00
30 changed files with 936 additions and 17 deletions
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Hxv3MkkZbMrKtIs6np9
ccP4OwGBkNhIvhPjcQP48hbbascv5RqsOquQGrYSD2ZrE/kbkRdkIcoHEeTZLif+
bDKFZFI7o5x0H92o9/GSvxHJhQ8mkmvwxD7lssGShwZEm8WG+U7BZqUV/gGmCDqe
9W8H8Fq2B0ck8IXjbQ4Zz+JlyV/NHZTZcs69plFiLKh4N6GYVftOVwSomh0bbypP
OB9WnLC7RC9a2LRrhtf8sqa2rRFmtyMMfgFFzLMzS+w+1K4+QLnWP1gKQVzaFnzk
pnwKPrqbGFYbRztIVEWbs8jPYlLkGb8ME4C84YVtQgbQcbyisU/VW3wUGkhT+J0k
xwIDAQAB
-----END PUBLIC KEY-----
+2
View File
@@ -203,6 +203,8 @@ gem 'request_store'
gem "virtus"
gem 'addressable'
gem "gitlab-license", "~> 0.0.2"
group :development do
gem 'brakeman', require: false
gem "annotate", "~> 2.6.0.beta2"
+2
View File
@@ -209,6 +209,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (~> 1.15)
posix-spawn (~> 0.3)
gitlab-license (0.0.2)
gitlab-linguist (3.0.1)
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4)
@@ -708,6 +709,7 @@ DEPENDENCIES
github-markup
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-grack (~> 2.0.2)
gitlab-license (~> 0.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.1.10)
@@ -114,6 +114,8 @@ if location.hash
setTimeout shiftWindow, 1
window.addEventListener "hashchange", shiftWindow
$.timeago.settings.allowFuture = true
$ ->
# Click a .js-select-on-focus field, select the contents
$(".js-select-on-focus").on "focusin", -> $(this).select()
@@ -0,0 +1,69 @@
class Admin::LicensesController < Admin::ApplicationController
before_action :license, only: [:show, :download, :destroy]
before_action :require_license, only: [:show, :download, :destroy]
respond_to :html
def show
@previous_licenses = License.previous
end
def download
send_data @license.data, filename: @license.data_filename, disposition: 'attachment'
end
def new
@license = License.new
end
def create
unless params[:license]
flash.now[:alert] = "No license was selected."
@license = License.new
render :new
return
end
@license = License.new(license_params)
respond_with(@license, location: admin_license_path) do
if @license.save
flash[:notice] = "The license was successfully uploaded and is now active. You can see the details below."
end
end
end
def destroy
license.destroy
message = "The license was removed. "
if License.current
flash[:notice] = "The license was removed. GitLab has fallen back on the previous license."
else
flash[:alert] = "The license was removed. GitLab now no longer has a valid license."
end
redirect_to admin_license_path
end
private
def license
@license ||= begin
License.reset_current
License.current
end
end
def require_license
return if license
flash.keep
redirect_to new_admin_license_path
end
def license_params
params.require(:license).permit(:data_file)
end
end
+9 -3
View File
@@ -213,9 +213,15 @@ module ApplicationHelper
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
capture_haml do
haml_tag :time, date.to_s,
class: html_class, datetime: date.getutc.iso8601, title: date.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
if date.is_a?(Date)
haml_tag :time, date.to_s,
class: html_class, datetime: date.iso8601, title: date.stamp('Aug 21, 2011'),
data: { toggle: 'tooltip', placement: placement }
else
haml_tag :time, date.to_s,
class: html_class, datetime: date.getutc.iso8601, title: date.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
end
haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
end.html_safe
+70
View File
@@ -0,0 +1,70 @@
module LicenseHelper
def license_message(signed_in: signed_in?, is_admin: (current_user && current_user.is_admin?))
@license_message ||=
if License.current
yes_license_message(signed_in, is_admin)
else
no_license_message(signed_in, is_admin)
end
end
private
def no_license_message(signed_in, is_admin)
message = []
message << "No GitLab Enterprise Edition license has been provided yet."
message << "Pushing code and creation of issues and merge requests has been disabled."
message <<
if is_admin
"Upload a license in the admin area"
else
"Ask an admin to upload a license"
end
message << "to activate this functionality."
message.join(" ")
end
def yes_license_message(signed_in, is_admin)
license = License.current
return unless signed_in
return unless (license.notify_admins? && is_admin) || license.notify_users?
message = []
message << "The GitLab Enterprise Edition license"
message << (license.expired? ? "expired" : "will expire")
message << "on #{license.expires_at}."
if license.expired? && license.will_block_changes?
message << "Pushing code and creation of issues and merge requests"
message <<
if license.block_changes?
"has been disabled."
else
"will be disabled on #{license.block_changes_at}."
end
end
message <<
if is_admin
"Upload a new license in the admin area"
else
"Ask an admin to upload a new license"
end
message << "to"
message << (license.block_changes? ? "restore" : "ensure uninterrupted")
message << "service."
message.join(" ")
end
extend self
end
+28 -12
View File
@@ -5,18 +5,34 @@ class Ability
return [] unless user.kind_of?(User)
return [] if user.blocked?
case subject.class.name
when "Project" then project_abilities(user, subject)
when "Issue" then issue_abilities(user, subject)
when "Note" then note_abilities(user, subject)
when "ProjectSnippet" then project_snippet_abilities(user, subject)
when "PersonalSnippet" then personal_snippet_abilities(user, subject)
when "MergeRequest" then merge_request_abilities(user, subject)
when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject)
when "GroupMember" then group_member_abilities(user, subject)
else []
end.concat(global_abilities(user))
abilities =
case subject.class.name
when "Project" then project_abilities(user, subject)
when "Issue" then issue_abilities(user, subject)
when "Note" then note_abilities(user, subject)
when "ProjectSnippet" then project_snippet_abilities(user, subject)
when "PersonalSnippet" then personal_snippet_abilities(user, subject)
when "MergeRequest" then merge_request_abilities(user, subject)
when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject)
when "GroupMember" then group_member_abilities(user, subject)
else []
end
abilities.concat(global_abilities(user))
abilities -= license_blocked_abilities if License.block_changes?
abilities
end
def license_blocked_abilities
[
:push_code,
:push_code_to_protected_branches,
:write_issue,
:write_merge_request
]
end
# List of possible abilities
+112
View File
@@ -0,0 +1,112 @@
class License < ActiveRecord::Base
validate :valid_license
validate :active_user_count, unless: :persisted?
validate :not_expired, unless: :persisted?
before_validation :reset_license, if: :data_changed?
after_create :reset_current
after_destroy :reset_current
scope :previous, -> { order(created_at: :desc).offset(1) }
class << self
def current
return @current if @current
license = self.last
return unless license && license.valid?
@current = license
end
def reset_current
@current = nil
end
def block_changes?
!current || current.block_changes?
end
end
def data_filename
company_name = self.licensee["Company"] || self.licensee.values.first
clean_company_name = company_name.gsub(/[^A-Za-z0-9]/, "")
"#{clean_company_name}.gitlab-license"
end
def data_file=(file)
self.data = file.read
end
def license
return nil unless self.data
@license ||=
begin
Gitlab::License.import(self.data)
rescue Gitlab::License::ImportError
nil
end
end
def license?
self.license && self.license.valid?
end
def method_missing(method_name, *arguments, &block)
if License.column_names.include?(method_name.to_s)
super
elsif license && license.respond_to?(method_name)
license.send(method_name, *arguments, &block)
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
if License.column_names.include?(method_name.to_s)
super
elsif license && license.respond_to?(method_name)
true
else
super
end
end
private
def reset_current
self.class.reset_current
end
def reset_license
@license = nil
end
def valid_license
return if license?
self.errors.add(:base, "The license file is invalid. Make sure it is exactly as you received it from GitLab B.V.")
end
def active_user_count
return unless self.license? && self.restricted?(:active_user_count)
restricted_user_count = self.restrictions[:active_user_count]
active_user_count = User.active.count
return if active_user_count <= restricted_user_count
message = "This license allows #{restricted_user_count} active users. "
message << "This GitLab installation currently has #{active_user_count}, "
message << "i.e. #{active_user_count - restricted_user_count} too many."
self.errors.add(:base, message)
end
def not_expired
return unless self.license? && self.expired?
self.errors.add(:base, "This license has already expired.")
end
end
+21
View File
@@ -0,0 +1,21 @@
- page_title "Upload License"
%h3.page-title Upload License
%p.light
To #{License.current ? "continue" : "start"} using GitLab Enterprise Edition, upload the <code>.gitlab-license</code> file you have received from GitLab B.V..
%hr
= form_for @license, url: admin_license_path, html: { multipart: true, class: 'form-horizontal fieldset-form' } do |f|
- if @license.errors.any?
#error_explanation
.alert.alert-danger
- @license.errors.full_messages.each do |msg|
%p= msg
.form-group
= f.label :data_file, "License", class: 'control-label col-sm-2'
.col-sm-10
= f.file_field :data_file, accept: ".gitlab-license,.gitlab_license,.txt"
.form-actions
= f.submit 'Upload license', class: 'btn btn-primary'
+121
View File
@@ -0,0 +1,121 @@
- page_title "License"
%h3.page-title
Your License
= link_to 'Upload New License', new_admin_license_path, class: "btn btn-new pull-right"
%hr
.row
.col-md-6
.panel.panel-default
.panel-heading
Licensed to
%ul.well-list
- @license.licensee.each do |label, value|
%li
%span.light #{label}:
%strong= value
.panel.panel-default
.panel-heading
Details
%ul.well-list
%li
%span.light Issued:
%strong= time_ago_with_tooltip @license.issued_at
%li
%span.light Uploaded:
%strong= time_ago_with_tooltip @license.created_at
%li
%span.light
- if @license.expired?
Expired:
- else
Expires:
%strong
- if @license.will_expire?
= time_ago_with_tooltip @license.expires_at
- if @license.expired?
%span.label.label-danger.pull-right
%strong Expired
- else
Never
.panel.panel-default
.panel-heading
Restrictions
%ul.well-list
%li
%span.light Active users:
%strong
- if @license.restricted?(:active_user_count)
- restricted_user_count = @license.restrictions[:active_user_count]
- active_user_count = User.active.count
#{restricted_user_count} users
- if active_user_count > restricted_user_count
%span.label.label-danger.pull-right
%strong Exceeded by #{active_user_count - restricted_user_count} users
- elsif restricted_user_count > active_user_count
%span.label.label-success.pull-right
%strong #{restricted_user_count - active_user_count} more allowed
- else
%span.label.label-info.pull-right
%strong Right at the limit
- else
Unlimited
.col-md-6
.panel.panel-info
.panel-heading
Download license
.panel-body
%p Your license will be included in your GitLab backup and will survive upgrades, so in normal usage you should never need to re-upload your <code>.gitlab-license</code> file.
%p Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab B.V. to send it to you again.
%br
= link_to 'Download license', download_admin_license_path, class: "btn btn-info"
.panel.panel-danger
.panel-heading
Remove license
.panel-body
%p If you remove this license, GitLab will fall back on the previous license, if any.
%p If there is no previous license or if the previous license has expired, some GitLab functionality will be blocked until a new, valid license is uploaded.
%br
= link_to 'Remove license', admin_license_path, data: { confirm: "Are you sure you want to remove the license?" }, method: :delete, class: "btn btn-remove"
- if @previous_licenses.any?
%h4 License History
.panel.panel-default#license_history
%table.table
%thead.panel-heading
%tr
- @license.licensee.keys.each do |label|
%th= label
%th Issued at
%th Uploaded at
%th Expired at
%th Active users
%tbody
- @previous_licenses.each do |license|
%tr
- @license.licensee.keys.each do |label|
%td= license.licensee[label]
%td
%span
= license.issued_at
%td
%span
= license.created_at
%td
%span
= license.expires_at || "Never"
%td
%span
- if license.restricted?(:active_user_count)
#{license.restrictions[:active_user_count]} users
- else
Unlimited
+6 -1
View File
@@ -1,4 +1,9 @@
- if broadcast_message.present?
.broadcast-message{ style: broadcast_styling(broadcast_message) }
%i.fa.fa-bullhorn
= icon('bullhorn')
= broadcast_message.message
- if license_message.present?
.broadcast-message
= icon('bullhorn')
= license_message
+6
View File
@@ -72,3 +72,9 @@
= icon('cogs fw')
%span
Settings
= nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License', data: {placement: 'right'} do
= icon('check fw')
%span
License
+15
View File
@@ -0,0 +1,15 @@
begin
public_key_file = File.read(Rails.root.join(".license_encryption_key.pub"))
public_key = OpenSSL::PKey::RSA.new(public_key_file)
Gitlab::License.encryption_key = public_key
rescue
warn "WARNING: No valid license encryption key provided."
end
# Needed to run migration
if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('licenses')
message = LicenseHelper.license_message(signed_in: true, is_admin: true)
if message.present?
warn "WARNING: #{message}"
end
end
+4
View File
@@ -211,6 +211,10 @@ Gitlab::Application.routes.draw do
resources :services
end
resource :license, only: [:show, :new, :create, :destroy] do
get :download, on: :member
end
root to: 'dashboard#index'
end
@@ -0,0 +1,9 @@
class CreateLicenses < ActiveRecord::Migration
def change
create_table :licenses do |t|
t.text :data, null: false
t.timestamps
end
end
end
+6
View File
@@ -211,6 +211,12 @@ ActiveRecord::Schema.define(version: 20150502064022) do
t.string "provider"
end
create_table "licenses", force: true do |t|
t.text "data", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "members", force: true do |t|
t.integer "access_level", null: false
t.integer "source_id", null: false
+44
View File
@@ -0,0 +1,44 @@
@admin
Feature: Admin license
Background:
Given I sign in as an admin
Scenario: Viewing current license
Given there is a license
And I visit admin license page
Then I should see to whom the license is licensed
Scenario: Viewing license when there is none
Given I visit admin license page
Then I should see a warning telling me there is no license
And I should be redirected to the license upload page
Scenario: Viewing expired license
Given there is a license
And the current license is expired
And I visit admin license page
Then I should see a warning telling me the license has expired
Scenario: Viewing license that blocks changes
Given there is a license
And the current license is expired
And the current license blocks changes
And I visit admin license page
Then I should see a warning telling me code pushes have been disabled
Scenario: Viewing license history
Given there is a license
And there are multiple licenses
And I visit admin license page
Then I should see to whom the licenses were licensed
Scenario: Uploading valid license
Given I visit admin upload license page
And I upload a valid license
Then I should see a notice telling me the license was uploaded
And I should see to whom the license is licensed
Scenario: Uploading invalid license
Given I visit admin upload license page
Then I upload an invalid license
Then I should see a warning telling me it's invalid
+86
View File
@@ -0,0 +1,86 @@
class Spinach::Features::AdminLicense < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
step 'I should see to whom the license is licensed' do
expect(page).to have_content(license.licensee.values.first)
end
step 'there is a license' do
create(:license)
end
step 'I should see a warning telling me there is no license' do
expect(page).to have_content "No GitLab Enterprise Edition license has been provided yet."
end
step 'I should be redirected to the license upload page' do
expect(current_path).to eq(new_admin_license_path)
end
step 'the current license is expired' do
build(:license, data: build(:gitlab_license, expires_at: Date.yesterday).export).save(validate: false)
end
step 'I should see a warning telling me the license has expired' do
expect(page).to have_content "The GitLab Enterprise Edition license expired"
end
step 'the current license blocks changes' do
build(:license, data: build(:gitlab_license, expires_at: Date.yesterday, block_changes_at: Date.today).export).save(validate: false)
end
step 'I should see a warning telling me code pushes have been disabled' do
expect(page).to have_content "Pushing code and creation of issues and merge requests has been disabled."
end
step 'there are multiple licenses' do
create(:license)
create(:license)
end
step 'I should see to whom the licenses were licensed' do
license_history = page.find("#license_history")
License.previous.each do |license|
expect(license_history).to have_content(license.licensee.values.first)
end
end
step 'I visit admin upload license page' do
visit new_admin_license_path
end
step 'I upload a valid license' do
path = Rails.root.join("tmp/valid_license.gitlab-license")
license = build(:gitlab_license)
File.write(path, license.export)
attach_file 'license_data_file', path
click_button "Upload license"
end
step 'I should see a notice telling me the license was uploaded' do
expect(page).to have_content "The license was successfully uploaded and is now active."
end
step 'I upload an invalid license' do
path = Rails.root.join("tmp/invalid_license.gitlab-license")
license = build(:gitlab_license, expires_at: Date.yesterday)
File.write(path, license.export)
attach_file 'license_data_file', path
click_button "Upload license"
end
step "I should see a warning telling me it's invalid" do
expect(page).to have_content "This license has already expired."
end
def license
License.reset_current
License.current
end
end
+4
View File
@@ -203,6 +203,10 @@ module SharedPaths
visit admin_git_hooks_path
end
step 'I visit admin license page' do
visit admin_license_path
end
# ----------------------------------------
# Generic Project
# ----------------------------------------
+3 -1
View File
@@ -16,7 +16,7 @@ require 'sidekiq/testing/inline'
require_relative 'capybara'
require_relative 'db_cleaner'
%w(select2_helper test_env repo_helpers).each do |f|
%w(select2_helper test_env repo_helpers license).each do |f|
require Rails.root.join('spec', 'support', f)
end
@@ -26,7 +26,9 @@ WebMock.allow_net_connect!
Spinach.hooks.before_run do
include RSpec::Mocks::ExampleMethods
TestEnv.init(mailer: false)
TestLicense.init
include FactoryGirl::Syntax::Methods
end
+5
View File
@@ -113,6 +113,11 @@ module Gitlab
unless project.repository.exists?
return build_status_object(false, "Repository does not exist")
end
if ::License.block_changes?
message = ::LicenseHelper.license_message(signed_in: true, is_admin: (user && user.is_admin?))
return build_status_object(false, message)
end
changes = changes.lines if changes.kind_of?(String)
+13
View File
@@ -202,4 +202,17 @@ FactoryGirl.define do
provider 'ldapmain'
extern_uid 'my-ldap-id'
end
factory :gitlab_license, class: "Gitlab::License" do
issued_at { Date.today }
licensee do
{ "Name" => Faker::Name.name }
end
notify_users_at { |l| l.expires_at }
notify_admins_at { |l| l.expires_at }
end
factory :license do
data { build(:gitlab_license).export }
end
end
@@ -224,4 +224,32 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context "when license blocks changes" do
before do
allow(License).to receive(:block_changes?).and_return(true)
end
describe "GET /:project_path/issues/new" do
subject { new_namespace_project_issue_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
@@ -202,4 +202,32 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context "when license blocks changes" do
before do
allow(License).to receive(:block_changes?).and_return(true)
end
describe "GET /:project_path/issues/new" do
subject { new_namespace_project_issue_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
@@ -229,4 +229,32 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context "when license blocks changes" do
before do
allow(License).to receive(:block_changes?).and_return(true)
end
describe "GET /:project_path/issues/new" do
subject { new_namespace_project_issue_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
+21
View File
@@ -231,6 +231,27 @@ describe Gitlab::GitAccess do
end
end
end
context "when license blocks changes" do
before do
allow(License).to receive(:block_changes?).and_return(true)
end
permissions_matrix.keys.each do |role|
describe "#{role} access" do
before { protect_feature_branch }
before { project.team << [user, role] }
permissions_matrix[role].each do |action, allowed|
context action do
subject { access.push_access_check(changes[action]) }
it { expect(subject.allowed?).to be_falsey }
end
end
end
end
end
end
describe "git_hook_check" do
+174
View File
@@ -0,0 +1,174 @@
require "spec_helper"
describe License do
let(:gl_license) { build(:gitlab_license) }
let(:license) { build(:license, data: gl_license.export) }
describe "Validation" do
describe "Valid license" do
context "when the license is provided" do
it "is valid" do
expect(license).to be_valid
end
end
context "when no license is provided" do
before do
license.data = nil
end
it "is invalid" do
expect(license).to_not be_valid
end
end
end
describe "Active user count" do
context "when there is no active user count restriction" do
it "is valid" do
expect(license).to be_valid
end
end
context "when the active user count restriction is exceeded" do
before do
gl_license.restrictions = { active_user_count: User.active.count - 1 }
end
it "is invalid" do
expect(license).to_not be_valid
end
end
context "when the active user count restriction is not exceeded" do
before do
gl_license.restrictions = { active_user_count: User.active.count + 1 }
end
it "is valid" do
expect(license).to be_valid
end
end
end
describe "Not expired" do
context "when the license doesn't expire" do
it "is valid" do
expect(license).to be_valid
end
end
context "when the license has expired" do
before do
gl_license.expires_at = Date.yesterday
end
it "is valid" do
expect(license).to_not be_valid
end
end
context "when the license has yet to expire" do
before do
gl_license.expires_at = Date.tomorrow
end
it "is valid" do
expect(license).to be_valid
end
end
end
end
describe "Class methods" do
let!(:license) { License.last }
before do
License.reset_current
allow(License).to receive(:last).and_return(license)
end
describe ".current" do
context "when there is no license" do
let!(:license) { nil }
it "returns nil" do
expect(License.current).to be_nil
end
end
context "when the license is invalid" do
before do
allow(license).to receive(:valid?).and_return(false)
end
it "returns nil" do
expect(License.current).to be_nil
end
end
context "when the license is valid" do
it "returns the license" do
expect(License.current)
end
end
end
describe ".block_changes?" do
context "when there is no current license" do
before do
allow(License).to receive(:current).and_return(nil)
end
it "returns true" do
expect(License.block_changes?).to be_truthy
end
end
context "when the current license is set to block changes" do
before do
allow(license).to receive(:block_changes?).and_return(true)
end
it "returns true" do
expect(License.block_changes?).to be_truthy
end
end
context "when the current license doesn't block changes" do
it "returns false" do
expect(License.block_changes?).to be_falsey
end
end
end
end
describe "#license" do
context "when no data is provided" do
before do
license.data = nil
end
it "returns nil" do
expect(license.license).to be_nil
end
end
context "when corrupt license data is provided" do
before do
license.data = "whatever"
end
it "returns nil" do
expect(license.license).to be_nil
end
end
context "when valid license data is provided" do
it "returns the license" do
expect(license.license).to_not be_nil
end
end
end
end
+4
View File
@@ -37,6 +37,10 @@ RSpec.configure do |config|
config.before(:suite) do
TestEnv.init
end
config.before(:all) do
TestLicense.init
end
end
ActiveRecord::Migration.maintain_test_schema!
+7
View File
@@ -0,0 +1,7 @@
class TestLicense
def self.init
Gitlab::License.encryption_key = OpenSSL::PKey::RSA.generate(2048)
FactoryGirl.create(:license)
end
end