ee token reprieve support

pull/9689/head
Markus Kahl 3 years ago
parent 72b1777f2c
commit e562d612b7
  1. 2
      Gemfile
  2. 6
      app/models/enterprise_token.rb
  3. 7
      app/views/enterprises/_current.html.erb
  4. 2
      config/locales/js-en.yml
  5. 12
      frontend/src/app/components/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts
  6. 4
      frontend/src/app/components/enterprise/enterprise-active-trial/ee-active-trial.base.ts
  7. 1
      frontend/src/app/components/enterprise/enterprise-active-trial/ee-active-trial.component.html
  8. 6
      frontend/src/app/components/enterprise/enterprise-active-trial/ee-active-trial.component.sass
  9. 42
      spec/fixtures/ee_tokens/v1_expired_with_7_days_reprieve_at_2021_09_01.token
  10. 42
      spec/fixtures/ee_tokens/v1_expired_without_reprieve_at_2021_09_01.token
  11. 86
      spec/views/admin/enterprises/_current.html.erb_spec.rb

@ -180,7 +180,7 @@ gem 'aws-sdk-core', '~> 3.107'
# File upload via fog + screenshots on travis
gem 'aws-sdk-s3', '~> 1.91'
gem 'openproject-token', '~> 2.1.1'
gem 'openproject-token', '~> 2.2.0'
gem 'plaintext', '~> 0.3.2'

@ -69,6 +69,8 @@ class EnterpriseToken < ApplicationRecord
:issued_at,
:starts_at,
:expires_at,
:reprieve_days,
:reprieve_days_left,
:restrictions,
to: :token_object
@ -86,8 +88,8 @@ class EnterpriseToken < ApplicationRecord
RequestStore.delete :current_ee_token
end
def expired?
token_object.expired? || invalid_domain?
def expired?(reprieve: true)
token_object.expired?(reprieve: reprieve) || invalid_domain?
end
##

@ -5,10 +5,13 @@
data-domain="<%= @current_token.try(:domain) %>"
data-user-count="<%= @current_token.restrictions.nil? ? t('js.admin.enterprise.upsale.unlimited') : @current_token.restrictions[:active_user_count] %>"
data-starts-at="<%= format_date @current_token.starts_at %>"
data-expires-at="<%= (!@current_token.will_expire?) ? t('js.admin.enterprise.upsale.unlimited') : (format_date @current_token.expires_at) %>">
data-expires-at="<%= (!@current_token.will_expire?) ? t('js.admin.enterprise.upsale.unlimited') : (format_date @current_token.expires_at) %>"
data-is-expired="<%= @current_token.expired?(reprieve: false) %>"
data-reprieve-days-left="<%= @current_token.reprieve_days_left %>"
>
</enterprise-active-saved-trial>
<%= form_tag({}, method: :delete) do %>
<%= form_tag(enterprise_path, method: :delete) do %>
<confirm-form-submit></confirm-form-submit>
<%= styled_button_tag t(:button_delete),
method: :delete,

@ -209,6 +209,8 @@ en:
new_group: 'New group'
reset_to_defaults: 'Reset to defaults'
enterprise:
text_reprieve_days_left: "%{days} days until end of grace period"
text_expired: "expired"
trial:
confirmation: "Confirmation of email address"
confirmation_info: >

@ -45,9 +45,21 @@ export class EEActiveSavedTrialComponent extends EEActiveTrialBase {
public userCount = this.elementRef.nativeElement.dataset['userCount'];
public startsAt = this.elementRef.nativeElement.dataset['startsAt'];
public expiresAt = this.elementRef.nativeElement.dataset['expiresAt'];
public isExpired:boolean = this.elementRef.nativeElement.dataset['isExpired'] == 'true';
public reprieveDaysLeft = this.elementRef.nativeElement.dataset['reprieveDaysLeft'];
constructor(readonly elementRef:ElementRef,
readonly I18n:I18nService) {
super(I18n);
}
public get expiredWarningText():string {
var warning = this.text.text_expired;
if (this.reprieveDaysLeft && this.reprieveDaysLeft > 0) {
warning = warning + ": " + this.text.text_reprieve_days_left(this.reprieveDaysLeft);
}
return warning;
}
}

@ -37,7 +37,9 @@ export class EEActiveTrialBase extends UntilDestroyedMixin {
label_company: this.I18n.t('js.admin.enterprise.trial.form.label_company'),
label_domain: this.I18n.t('js.admin.enterprise.trial.form.label_domain'),
label_starts_at: this.I18n.t('js.admin.enterprise.trial.form.label_starts_at'),
label_subscriber: this.I18n.t('js.admin.enterprise.trial.form.label_subscriber')
label_subscriber: this.I18n.t('js.admin.enterprise.trial.form.label_subscriber'),
text_expired: this.I18n.t('js.admin.enterprise.text_expired'),
text_reprieve_days_left: (days:string) => this.I18n.t('js.admin.enterprise.text_reprieve_days_left', { days: days })
};
constructor(readonly I18n:I18nService) {

@ -42,6 +42,7 @@
<div *ngIf="expiresAt" class="attributes-key-value--value-container">
<div class="attributes-key-value--value -text">
<span [textContent]="expiresAt"></span>
<span *ngIf="isExpired" [textContent]="expiredWarningText" class="expired-warning"></span>
</div>
</div>
</div>

@ -1,2 +1,6 @@
.attributes-group
margin-bottom: 1.25rem
margin-bottom: 1.25rem
.expired-warning
margin-left: 1rem
color: red
font-weight: bold

@ -0,0 +1,42 @@
-----BEGIN OPENPROJECT-EE TOKEN-----
eyJkYXRhIjoiMjhjK0xKa2wzS3NrUkVOdXlIWmZBMEg5bHRnbjRIQmxCV0NI
SFBZeDF5YXhvL01qT25Ud1JxUUo1bzlPXG5FaUN0MlRYTzJGNE1PdXhXdUtD
MHo5ek5xZFVHUTlFM3hrbGJGdHpSWldKbmZCTnd4WGIvSzY3NFhDZTlcbmhY
ZmhCcmJlVnFDOVV2Vm5jSExaelVwWVc2ZHpEbmdpRUorV2JlWFdJOEFwbFZj
VWxYZTRZUlRxREl0S1xuYUc4NHVjMDM0SmVHQjNyMS9hQnhnVVhrcFZoZXEy
aHR2VEJ6NWdNRkRBMExjMVNDL2E0K3Q0TzlMZmh6XG5vODJLdUF3MitGNlR2
bG5nbE1JYzdLTm9OdmZFYWkzT0ZXc2FjVHFTcEk3NjJvVlJGbVU5aS9Ramhn
NXVcblI1bkdkY3ZMdjM0N1VlemY0OWdaXG4iLCJrZXkiOiJlWFhCZll2QjV4
cmVBSEg1Lyt5RVlWaDZCV2lmRU1DdnRlc2xMdWIzQlNQd3BKNkZCc1YzbEhy
bS81WUlcbm9nZUJYSkVwMXY5SnU5K1Nvck43UW9OMWxQS3F3TWRsTGpmSlY0
VHZneUVNWng5Sk1wTVdndXVXTkI1blxuT004RXhBd1dxL3ArUTF2OExYem5t
ZTZDK05CMmh6c0ovVHovL2JSazBGaVNaWnFDcEFXNmtwZDZ2M2tVXG5nODJT
SmdHOWNKd3Z5djE2VHJPc1owM2lzRE0xR3pmWmZIMVZKa00waHhLTFJpUjZO
Z25qUXBzbGhWc2dcbmxmVFNiZjhPR3N3NHBpUTZZRXh1ZkJTZ1RQcWpPc0Qr
dU9FYUYzTzB0NzYrZ3dJUnZDMWh4WWJibDBPUFxuRkwwaGI2WG13RjVIUE8x
KzZrSlNCZCtQMWViK3RiUDJjMDZCKzZmZ04rckZNSEFtd1NKMHBtdEVhRjRC
XG5CbEM1Z3JmbE9lemhpbkRBdXFIWFVyMk1mdjIzUHpNMjhXU3V2cGZsTUcz
clhyL2JJZjAxY3Rsc3lQRWJcbnlha1h4bVJRaTNLeWExT21PbWthV3BpVjEy
OEpiREpvN2g3cjBkZ0RoRDRFcExGNm9RaWxmYnA2MUdDQlxuQ1JqYm81cFNJ
Y0RUV1Z6SGZDN25jQmgvQW5vSDE2cTRMMzVqODB0VXBQVXBxblp6b0J4ZDBn
NTQrWHNVXG5WU2hOZngxMTNzSXR3dWFKREg0Wm9sSGx0clZXbXUwNncvL3ZQ
TUJHQ1lCdXc3OEkvNTNDV2IxeHZINUxcbkt4dmxoaVhrSVhqdmg5L3FrdWJJ
T2FWUUNmbG1sYW02YnJMMDEwVE5mNGdlU2VXd0QzcTZkU09aVUZTNVxuSkpR
UlpQQ0U3L3haZ0FmR2pzOERZeE5oVHp5Y1ZmbkhDR01VbnNOeThYcWJ1QUxV
aFhHODRGenBmbDF3XG5Rb0JIdEREaTVXbVJPdTQxNjh0US9iYXhBUEZHUjdK
UUpuYTBIdE9qV0VwU2h5b21Ra3g4bWIxY01hdDhcbk5MN0dBM0xUYnRMUm05
UXVyTkpmblo0dkFhM1czb2pEaEpwZmFmcktDTjVQS1NTMmdNeWQyTEdpaEhr
VVxuSWhYQUJCSzBjWS9rYUlMZnA2SHcvdkk3SWczZ3NYNWRPQlFQQlY1SHJ6
T255eXFwVzZwb0ZPRkw0STNyXG5lV1pDWUd6ZEY2UmUxS0RKRVVSeVlSODdV
YVYreXpuVWFtaWV5ZHpoc0g5SUN4cVlNamt1NHdXYkVqdmZcbm9FY3ZGYi9T
WHEwVHBEemd5cDNHNm9tRFNuUFg2b3kwL2Ivb2ZzSHhrRXRVYS9NK3czWTdi
QTI0TUJUQ1xuTi8wZWRxV0s5b0IzWjdKaTBQVEU0cmk0R09ZckppNXdHRENw
WTlaN1JscGhmS2gzeC9leFZiTkFsUENYXG5DeEhxbUs2cXZLbG5Zei9QYVpa
dWFKZGlsTFJYM0luNno0NFBUWDBGTVNRNTFqcWwzNlZJWnZEUlNaTkJcbk05
Tnl3b2JJUkp3VncweVVLbDFhT0V4ODYzRjVKMThKK0VHa2dkMHR0YnZsNjRS
Vm1aSTZPM2lqdTdDMVxuanpIbzlDZjJRQi9uVk9SNldLTytWczZTTE5FcHpx
dGlHbU5jWUpPM3Mybit0QlUrL2xPMlBmbDQ0OVlCXG5rcGVBTUYvM0RERTVL
YzI3ZndpY1NENDFBWWl0c0llN01Eb1pUMG5wOXlVM2dxQUthMkhQd2dnMWcw
R0xcbjBGVEtoWkkwendTQkhwZU5rZzM5Q0VZUDVWQk8vdz09XG4iLCJpdiI6
IkREbjZ3cm1NczBGaFk5cVFtanNuQXc9PVxuIn0=
-----END OPENPROJECT-EE TOKEN-----

@ -0,0 +1,42 @@
-----BEGIN OPENPROJECT-EE TOKEN-----
eyJkYXRhIjoiR0hXTnJxS0VhZUhVeWozcks0elh4K3dTeEpMMGhPbVd0VVRK
cXpmY3UzUWFLcGZDQVd2Vmxhc2JEQ0F0XG5lQkZ5U0JiSUs5VkMyQXkzaFBD
VHl5dy9ibFY5ZXhaaGRqTjVyd1dVbUxpTHJ3czZqRkVtZnNmemJCUURcbkgv
aWtUSm5rRGU1RkRqeDBrR29iTmJSbVg5K1hjc2xYZjNFSEN5ZDV2TUpjd1dF
UDFQQW5WaUJmNWdEL1xuMDlLWW9BT1NBVDI5cDJUclVOb25ic0NqMjFJa1Vy
dUxFdGhSa2dpa2N2dHVXcldoQWpuWC9qUWNsOXoxXG5tRzlvVWJOUzU4dWN6
VHdSNysxU25SQ1dEK0tKNElUSG15bm5vODFQWVkxT21yckdiYjY5azZrSlFC
MkRcbmNqZTkvRkF3T1doSGxyMlRtNUxvXG4iLCJrZXkiOiJOU1NHYkR6aHoy
ekl6SkkreWFwdituaEhCREw0WmxZWU9XSldmcUJhcy9rLzF3QWpDZU1DMFRs
dm5maWRcbmxIMHh5ZjE3YVU2UWRqL0hua3ZZQXlMMTEvdVZvT2c2S1VwQ2VW
ZU1JNC9yZjBFZHI4Z1hyNEhMb2loT1xuWkNkdkc5ZjMxUEF3ZUpjUTJ2b3hr
eEJPMFhFdVErVnd6UVgzb2lneVNNUUJEck1QaGc5UTY5eXp6R3hJXG5teHJ6
YWY2bmdzSk9aS1FnVlllamtpVmdCUnBuUm16T1VOb0U0cXU4Njh4cm9xOUZp
RkorK0JUMnUxc01cbllvOFhNSC9ndGltSTF6YUltdVF4TTk1bE9IcTNLM0hr
UFhoY3ZxT1ZwcW9sUnQ1RE1KelEvOTBMNkFraFxuK3ZXU1hOMThSZXhHUklR
RDBRVUcreWVnSUtxTFY0VHljMEdzNjhYYldoWXQxdytTTlpIank1b1F4MGZq
XG5oU3ZuVzZUdzd4YmtKMWJoUmk0dHpxM1VmNlovUEVDeXZ0ZllOcHFiUDZ3
N1JQMjBFY3ZTTVR6ZU0yQUNcbjMvcEw5YzNobVVPVzM4dEc3cnJ6cnpLc0ww
M25FZ2NuaVhHZ0J5YTVPdVBsS1JQcG4zdUs1NFVUbmRQaVxuWEhRWjd1WHFC
ekNlNzRDRzJjaHQ3RGxRVDQwdlczMkp0K3RCRDBLakZveWtjajV5dWw5Q1hx
K2VDN0Y5XG5VSXNKR3JRRDFYMGxmdjJ0THdxOTJ4RnhOejdwLzhoUVJ0OG5C
OTNpYWdnUTI4ekdQRTVwbW9WTnEzaW1cbmVlNFQxbktxTFNyRTRKUmRZdTZN
OHBhdk5ZRmFOdGFtbEcxRStvT0tUNG1kMGlNQ0Y0dnlRVzJQc0FuOVxuQVNq
M0lzSTlac0Vra1Q0dTc2cG9RSmliampJKzUxUWFoNnpUSHZLYlVnQ1FtUDla
SEZzajhDdmRabGU2XG44dkUvZmlRd3Z2R2dXaEpFaG96WWEvcW5yQmFWbmd5
Y1ZqSUFLTm9UbGFyWEYyRDliSjlQUXJJYzdGb0JcbnNBRTY1MDBhM3ZqNzA1
c2FXcm1kVGRjdXdRU1M3QzY4TDJ4TnFaUlh6T2tRVlR0aWEwUnd0R2xKZm1R
RlxuQ3l3c0tVUHRnbXA4KzNlUUtRWEE4ZTMvdWZOTXhvSENUQ3p6M3ZJaUtY
Y1pVWFRPcEJCVisybXJxOTArXG53eTZ1Q0Y2TkRKVCtkQUU3aE1hMzhSTzhq
cXFVeGR6bktCemJFUDdwNHdWK1Yvb053ajV3SWdhZzBXYm5cblcwUkhpcURx
eVJMNzVNa29JT0g2dndlOW1VbGJ5Q3Fwa1FITDd0Wk45bWZJcXcxWUs5NzY4
a1czcE1BQVxuMElESDJBRHVTS09NNkpZcjRrYURVNHVNT0E2a1haOFphMmdS
ZUZPYTBxblI5V2E2WFVPeE4vRS9CNVNoXG5qa2RkNWdweGp3MlExa2I5RFhL
TmhJNStjcTF4ekZrMTRReFJjSUNlaTJtWUVtR0w4Rml0SlUxbFg4a2tcbnNO
d2dUbUdja2JaMDluT2FDdjFucUlCcGs5dW4zQnUwMGdiY3hGVGt2VldtTDdw
MVpvUVJJVGQva1FESlxucjBKc3Qvdnk2S1Bkek9xR0Z0M212enEvOUlJL3Br
T2ZuaHJwTWpZY1JkRXB0WVROUWoxVDNBTm82ekFmXG52MmhGM05EQnhMVXdh
bU9kMkljWStaWERVamc5ZGFpUEVLQ1FkMmtGNDJDWXdCKzNQcGw0azVmSk5h
WXFcbnd0SmxNTVkxODFka3Vzbm1qTDVmcmpoMmpnWkZidz09XG4iLCJpdiI6
IjNiM29OTVJCSHFiRGF3bGdtTEN5RVE9PVxuIn0=
-----END OPENPROJECT-EE TOKEN-----

@ -0,0 +1,86 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe 'admin/enterprises/_current', type: :view do
let(:current_user) { FactoryBot.create :admin }
let(:ee_token) { "v1_expired_with_7_days_reprieve_at_2021_09_01.token" }
let(:current_time) { DateTime.now }
before do
allow(User).to receive(:current).and_return current_user
encoded = File.read Rails.root.join("spec/fixtures/ee_tokens/#{ee_token}")
token = EnterpriseToken.new(encoded_token: encoded)
assign :current_token, token
Timecop.travel(current_date) do
render :partial => "enterprises/current"
end
end
context "with token still valid" do
let(:current_date) { "2021-08-28".to_datetime }
it "renders the token as not expired and with no reprieve days" do
expect(rendered.to_s).to include 'data-is-expired="false"'
expect(rendered.to_s).to include 'data-reprieve-days-left=""'
end
end
context "with token just expired (within grace period)" do
let(:current_date) { "2021-09-02".to_datetime }
it "renders the token as expired and with 6 reprieve days" do
expect(rendered.to_s).to include 'data-is-expired="true"'
expect(rendered.to_s).to include 'data-reprieve-days-left="6"'
end
end
context "with token expired past reprieve" do
let(:current_date) { "2021-09-08".to_datetime }
it "renders the token as expired and with 0 reprieve days" do
expect(rendered.to_s).to include 'data-is-expired="true"'
expect(rendered.to_s).to include 'data-reprieve-days-left="0"'
end
end
context "with token expired and no reprieve" do
let(:ee_token) { "v1_expired_without_reprieve_at_2021_09_01.token" }
let(:current_date) { "2021-09-08".to_datetime }
it "renders the token as expired and with no reprieve days" do
expect(rendered.to_s).to include 'data-is-expired="true"'
expect(rendered.to_s).to include 'data-reprieve-days-left=""'
end
end
end
Loading…
Cancel
Save