1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 21:29:42 +02:00

Accept activation key during setup process

Enterprise edition only.
This commit is contained in:
sauls8t 2019-06-19 12:46:05 +01:00
parent 887c999a1e
commit 4d2f30711c
11 changed files with 161 additions and 52 deletions

View file

@ -12,6 +12,8 @@
package database package database
import ( import (
"encoding/json"
"encoding/xml"
"errors" "errors"
"net/http" "net/http"
"time" "time"
@ -21,6 +23,7 @@ import (
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
"github.com/documize/community/core/uniqueid" "github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store" "github.com/documize/community/domain/store"
"github.com/documize/community/server/web" "github.com/documize/community/server/web"
) )
@ -73,6 +76,7 @@ func (h *Handler) Setup(w http.ResponseWriter, r *http.Request) {
Password: r.Form.Get("password"), Password: r.Form.Get("password"),
Firstname: r.Form.Get("firstname"), Firstname: r.Form.Get("firstname"),
Lastname: r.Form.Get("lastname"), Lastname: r.Form.Get("lastname"),
ActivationKey: r.Form.Get("activationKey"),
Revised: time.Now().UTC(), Revised: time.Now().UTC(),
} }
@ -116,6 +120,7 @@ type onboardRequest struct {
Password string Password string
Firstname string Firstname string
Lastname string Lastname string
ActivationKey string
Revised time.Time Revised time.Time
} }
@ -132,10 +137,14 @@ func setupAccount(rt *env.Runtime, completion onboardRequest, serial string) (er
salt := secrets.GenerateSalt() salt := secrets.GenerateSalt()
password := secrets.GeneratePassword(completion.Password, salt) password := secrets.GeneratePassword(completion.Password, salt)
// Process activation key if we have one.
activationKey := processActivationKey(rt, completion)
// Allocate organization to the user. // Allocate organization to the user.
orgID := uniqueid.Generate() orgID := uniqueid.Generate()
_, err = tx.Exec(RebindParams("INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, c_domain, c_email, c_serial) VALUES (?, ?, ?, ?, ?, ?, ?)", rt.StoreProvider.Type()), _, err = tx.Exec(RebindParams("INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, c_domain, c_email, c_serial, c_sub) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
orgID, completion.Company, completion.CompanyLong, completion.Message, completion.URL, completion.Email, serial) rt.StoreProvider.Type()),
orgID, completion.Company, completion.CompanyLong, completion.Message, completion.URL, completion.Email, serial, activationKey)
if err != nil { if err != nil {
rt.Log.Error("INSERT INTO dmz_org failed", err) rt.Log.Error("INSERT INTO dmz_org failed", err)
tx.Rollback() tx.Rollback()
@ -243,3 +252,30 @@ func setupAccount(rt *env.Runtime, completion onboardRequest, serial string) (er
return return
} }
func processActivationKey(rt *env.Runtime, or onboardRequest) (key string) {
key = "{}"
if len(or.ActivationKey) == 0 {
return
}
j := domain.SubscriptionData{}
x := domain.SubscriptionXML{Key: "", Signature: ""}
err1 := xml.Unmarshal([]byte(or.ActivationKey), &x)
if err1 == nil {
j.Key = x.Key
j.Signature = x.Signature
} else {
rt.Log.Error("failed to XML unmarshal subscription XML", err1)
}
d, err2 := json.Marshal(j)
if err2 == nil {
key = string(d)
} else {
rt.Log.Error("failed to JSON marshal subscription XML", err2)
}
return
}

View file

@ -0,0 +1,34 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { inject as service } from '@ember/service';
import Modals from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(Modals, {
localStorage: service(),
didReceiveAttrs() {
this._super(...arguments);
let firstRun = this.get('localStorage').isFirstRun();
if (firstRun) {
this.modalOpen('#first-run-modal', {'show': true});
}
},
actions: {
onCloseModal() {
this.modalClose('#first-run-modal');
}
}
});

View file

@ -7,6 +7,7 @@
<title>Documize</title> <title>Documize</title>
<meta property="dbname" content="{{.DBname}}" /> <meta property="dbname" content="{{.DBname}}" />
<meta property="dbhash" content="{{.DBhash}}" /> <meta property="dbhash" content="{{.DBhash}}" />
<meta property="edition" content="{{.Edition}}" />
<meta name="author" content="Documize" /> <meta name="author" content="Documize" />
<meta name="description" content="Documize"> <meta name="description" content="Documize">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

View file

@ -16,8 +16,12 @@ export default Route.extend({
session: service(), session: service(),
localStorage: service(), localStorage: service(),
beforeModel() { beforeModel(transition) {
this.get('localStorage').clearAll(); this.get('localStorage').clearAll();
if (!_.isUndefined(transition.to.queryParams.fr)) {
this.get('localStorage').setFirstRun();
}
}, },
model({ token }) { model({ token }) {

View file

@ -1,4 +1,4 @@
<div class="sso-box"> <div class="sso-box">
<p>Signing in...</p> <p>Signing in...</p>
<img src="/assets/img/busy-gray.gif"> <img src="/assets/img/busy-gray.gif" alt="busy">
</div> </div>

View file

@ -23,10 +23,10 @@ export default Controller.extend({
data: this.model, data: this.model,
dataType: "text", dataType: "text",
}).then(() => { }).then(() => {
let dom = ""; // supports http://localhost:5001 installations (which is the default for all self-installs) let dom = ""; // supports http://localhost:5001 installs (which is the default for all self-installs)
var credentials = Encoding.Base64.encode(dom + ":" + this.model.email + ":" + this.model.password); let credentials = Encoding.Base64.encode(dom + ":" + this.model.email + ":" + this.model.password);
window.location.href = "/auth/sso/" + encodeURIComponent(credentials); window.location.href = "/auth/sso/" + encodeURIComponent(credentials) + '?fr=1';
}).catch((error) => { // eslint-disable-line no-unused-vars }).catch((/*error*/) => {
// TODO notify user of the error within the GUI // TODO notify user of the error within the GUI
}); });
} }

View file

@ -33,7 +33,9 @@ export default Route.extend({
firstname: "", firstname: "",
lastname: "", lastname: "",
email: "", email: "",
password: pwd password: pwd,
activationKey: '',
edition: document.head.querySelector("[property=edition]").content
}; };
}, },

View file

@ -59,10 +59,12 @@ export default Service.extend({
let isInSetupMode = dbhash && dbhash !== "{{.DBhash}}"; let isInSetupMode = dbhash && dbhash !== "{{.DBhash}}";
if (isInSetupMode) { if (isInSetupMode) {
let edition = document.head.querySelector("[property=edition]");
this.setProperties({ this.setProperties({
title: htmlSafe("Documize Setup"), title: htmlSafe("Documize Setup"),
allowAnonymousAccess: true, allowAnonymousAccess: true,
setupMode: true setupMode: true,
edition: !_.isNull(edition) ? edition : 'Community'
}); });
this.get('localStorage').clearAll(); this.get('localStorage').clearAll();

View file

@ -12,22 +12,23 @@
import Service from '@ember/service'; import Service from '@ember/service';
export default Service.extend({ export default Service.extend({
storeSessionItem: function (key, data) { // Remove all items from storage.
localStorage[key] = data;
},
getSessionItem: function (key) {
return localStorage[key];
},
clearSessionItem: function (key) {
delete localStorage[key];
},
clearAll() { clearAll() {
localStorage.clear(); localStorage.clear();
}, },
// Generic session item CRUD methods.
storeSessionItem: function (key, data) {
localStorage[key] = data;
},
getSessionItem: function (key) {
return localStorage[key];
},
clearSessionItem: function (key) {
delete localStorage[key];
},
// Per document expand/collapse state.
getDocSectionHide(docId) { getDocSectionHide(docId) {
let state = localStorage[`doc-hide-${docId}`]; let state = localStorage[`doc-hide-${docId}`];
if (_.isUndefined(state) || _.isEmpty(state)) { if (_.isUndefined(state) || _.isEmpty(state)) {
@ -36,7 +37,6 @@ export default Service.extend({
return _.split(state, '|'); return _.split(state, '|');
}, },
setDocSectionHide(docId, state) { setDocSectionHide(docId, state) {
let key = `doc-hide-${docId}`; let key = `doc-hide-${docId}`;
@ -46,4 +46,15 @@ export default Service.extend({
localStorage[key] = _.join(state, '|'); localStorage[key] = _.join(state, '|');
} }
}, },
// First run wizard.
setFirstRun() {
localStorage['firstRun'] = true;
},
isFirstRun() {
return !_.isUndefined(localStorage['firstRun']);
},
unsetFirstRun() {
delete localStorage['firstRun'];
}
}); });

View file

@ -1,7 +1,7 @@
<div class="installer"> <div class="installer">
<img src="/assets/img/setup/logo-purple.png" srcset="/assets/img/setup/logo-purple@2x.png" alt="Documize Setup" style="width: 250px; height: 76px;"> <img src="/assets/img/setup/logo-purple.png" srcset="/assets/img/setup/logo-purple@2x.png" alt="Documize Setup" style="width: 250px; height: 76px;">
<Ui::UiSpacer @size="300" /> <Ui::UiSpacer @size="300" />
<h1 class="color-theme-700">Installation Guide</h1> <h1 class="color-theme-700">Setup</h1>
<p class="color-gray-800"> <p class="color-gray-800">
Review the <a href="https://docs.documize.com/s/VzO9ZqMOCgABGyfW/installation-guides/d/V16L08ucxwABhZF6/installation-guide">installation instructions</a> Review the <a href="https://docs.documize.com/s/VzO9ZqMOCgABGyfW/installation-guides/d/V16L08ucxwABhZF6/installation-guide">installation instructions</a>
and recommended <a href="https://docs.documize.com/s/VzO9ZqMOCgABGyfW/installation-guides/d/V2KuM8ICcQABagM5/mysql-specific-database-tuning">database tuning guide</a> and recommended <a href="https://docs.documize.com/s/VzO9ZqMOCgABGyfW/installation-guides/d/V2KuM8ICcQABagM5/mysql-specific-database-tuning">database tuning guide</a>
@ -11,27 +11,33 @@
<form {{action "save" on="submit"}}> <form {{action "save" on="submit"}}>
<div class="form-group"> <div class="form-group">
<label>Site name</label> <label>Site name</label>
{{focus-input id="siteTitle" type="text" value=model.title class=(if hasEmptyTitleError "form-control is-invalid" "form-control")}} {{focus-input id="setup-title" type="text" value=model.title class=(if hasTitleError "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">Usually your company or team name</small> <small class="form-text text-muted">Usually your company or team name</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="adminFirstname">Firstname</label> <label for="setup-firstname">Firstname</label>
{{input id="adminFirstname" type="text" value=model.firstname class=(if hasEmptyFirstnameError "form-control is-invalid" "form-control")}} {{input id="setup-firstname" type="text" value=model.firstname class=(if hasFirstnameError "form-control is-invalid" "form-control")}}
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="adminLastname">Lastname</label> <label for="setup-lastname">Lastname</label>
{{input id="adminLastname" type="text" value=model.lastname class=(if hasEmptyLastnameError "form-control is-invalid" "form-control")}} {{input id="setup-lastname" type="text" value=model.lastname class=(if hasLastnameError "form-control is-invalid" "form-control")}}
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="adminEmail">Email</label> <label for="setup-email">Email</label>
{{input id="adminEmail" type="email" value=model.email class=(if hasEmptyEmailError "form-control is-invalid" "form-control")}} {{input id="setup-email" type="email" value=model.email class=(if hasEmailError "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">No spam. Ever.</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="adminPassword">Password</label> <label for="new-password">Password</label>
{{input id="adminPassword" type="password" value=model.password class=(if hasEmptyPasswordError "form-control is-invalid" "form-control")}} {{input id="new-password" type="password" value=model.password class=(if hasPasswordError "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">Pick something strong and unique that you don't use anywhere else</small> <small class="form-text text-muted">Pick something strong and unique that you don't use anywhere else</small>
</div> </div>
{{#if (eq model.edition constants.Product.EnterpriseEdition)}}
<div class="form-group">
<label for="activation-key">Activation Key</label>
{{textarea id="activation-key" value=model.activationKey rows="5" class=(if hasKeyError "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">You can get it from <a href="https://www.documize.com/downloads" target="_blank">https://www.documize.com/downloads</a></small>
</div>
{{/if}}
{{ui/ui-button submit=true color=constants.Color.Green light=true label=buttonLabel onClick=(action "save")}} {{ui/ui-button submit=true color=constants.Color.Green light=true label=buttonLabel onClick=(action "save")}}
</form> </form>

View file

@ -0,0 +1,13 @@
<div id="first-run-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">Getting Started</div>
<div class="modal-body">
<p>dfdfdf</p>
</div>
<div class="modal-footer">
{{ui/ui-button color=constants.Color.Yellow light=true label="Got it!" dismiss=true}}
</div>
</div>
</div>
</div>