mirror of
https://github.com/documize/community.git
synced 2025-08-02 20:15:26 +02:00
PRovide basic in-app purchase/renewal flow
This commit is contained in:
parent
e116d3b000
commit
d1b803b246
39 changed files with 1211 additions and 1154 deletions
|
@ -206,7 +206,7 @@ func (b backerHandler) produce(id string) (files []backupItem, err error) {
|
|||
func (b backerHandler) manifest(id string) (string, error) {
|
||||
m := m.Manifest{
|
||||
ID: id,
|
||||
Edition: b.Runtime.Product.License.Edition,
|
||||
Edition: b.Runtime.Product.Edition,
|
||||
Version: b.Runtime.Product.Version,
|
||||
Major: b.Runtime.Product.Major,
|
||||
Minor: b.Runtime.Product.Minor,
|
||||
|
|
|
@ -449,6 +449,11 @@ func (r *restoreHandler) dmzConfig() (err error) {
|
|||
r.Runtime.Log.Info(fmt.Sprintf("Extracted %s", filename))
|
||||
|
||||
for i := range c {
|
||||
// We skip database schema version setting as this varies
|
||||
// between database providers (e.g. MySQL v26, PostgreSQL v2).
|
||||
if strings.ToUpper(c[i].ConfigKey) == "META" {
|
||||
continue
|
||||
}
|
||||
err = r.Store.Setting.Set(c[i].ConfigKey, c[i].ConfigValue)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("unable to insert %s %s", filename, c[i].ConfigKey))
|
||||
|
|
|
@ -39,7 +39,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
method := "block.add"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ import (
|
|||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// RequestContext provides per request scoped values required
|
||||
// by HTTP handlers.
|
||||
// RequestContext provides per request scoped values required for HTTP handlers.
|
||||
type RequestContext struct {
|
||||
AllowAnonymousAccess bool
|
||||
Authenticated bool
|
||||
|
@ -36,14 +35,13 @@ type RequestContext struct {
|
|||
Expires time.Time
|
||||
Fullname string
|
||||
Transaction *sqlx.Tx
|
||||
AppVersion string
|
||||
|
||||
Administrator bool
|
||||
Analytics bool
|
||||
Active bool
|
||||
Editor bool
|
||||
GlobalAdmin bool
|
||||
ViewUsers bool
|
||||
Administrator bool
|
||||
Analytics bool
|
||||
Active bool
|
||||
Editor bool
|
||||
GlobalAdmin bool
|
||||
ViewUsers bool
|
||||
Subscription Subscription
|
||||
}
|
||||
|
||||
//GetAppURL returns full HTTP url for the app
|
||||
|
|
|
@ -174,7 +174,7 @@ func processDocument(ctx domain.RequestContext, r *env.Runtime, store *store.Sto
|
|||
documentID := uniqueid.Generate()
|
||||
document.RefID = documentID
|
||||
|
||||
if r.Product.Edition == env.CommunityEdition {
|
||||
if r.Product.Edition == domain.CommunityEdition {
|
||||
document.Lifecycle = workflow.LifecycleLive
|
||||
} else {
|
||||
document.Lifecycle = sp.Lifecycle
|
||||
|
|
|
@ -83,7 +83,6 @@ func (store *LocalStorageProvider) Convert(params api.ConversionJobRequest) (fil
|
|||
defer func() { os.RemoveAll(inputFolder) }()
|
||||
|
||||
for _, v := range list {
|
||||
|
||||
if v.Size() > 0 && !strings.HasPrefix(v.Name(), ".") && v.Mode().IsRegular() {
|
||||
filename = inputFolder + v.Name()
|
||||
fileData, err := ioutil.ReadFile(filename)
|
||||
|
@ -100,8 +99,6 @@ func (store *LocalStorageProvider) Convert(params api.ConversionJobRequest) (fil
|
|||
fileRequest.LicenseKey = params.LicenseKey
|
||||
fileRequest.LicenseSignature = params.LicenseSignature
|
||||
fileRequest.ServiceEndpoint = params.ServiceEndpoint
|
||||
//fileRequest.Job = params.OrgID + string(os.PathSeparator) + params.Job
|
||||
//fileRequest.OrgID = params.OrgID
|
||||
|
||||
bits := strings.Split(filename, ".")
|
||||
xtn := strings.ToLower(bits[len(bits)-1])
|
||||
|
|
|
@ -60,10 +60,9 @@ func (h *Handler) Meta(w http.ResponseWriter, r *http.Request) {
|
|||
data.Version = h.Runtime.Product.Version
|
||||
data.Revision = h.Runtime.Product.Revision
|
||||
data.Edition = h.Runtime.Product.Edition
|
||||
data.Valid = h.Runtime.Product.License.IsValid(org.RefID)
|
||||
data.ConversionEndpoint = org.ConversionEndpoint
|
||||
data.License = h.Runtime.Product.License
|
||||
data.Storage = h.Runtime.StoreProvider.Type()
|
||||
data.Location = h.Runtime.Flags.Location // reserved
|
||||
|
||||
// Strip secrets
|
||||
data.AuthConfig = auth.StripAuthSecrets(h.Runtime, org.AuthProvider, org.AuthConfig)
|
||||
|
|
|
@ -31,10 +31,10 @@ type Store struct {
|
|||
}
|
||||
|
||||
// AddOrganization inserts the passed organization record into the organization table.
|
||||
func (s Store) AddOrganization(ctx domain.RequestContext, org org.Organization) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, c_domain, c_email, c_anonaccess, c_serial, c_maxtags, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||
org.RefID, org.Company, org.Title, org.Message, strings.ToLower(org.Domain),
|
||||
strings.ToLower(org.Email), org.AllowAnonymousAccess, org.Serial, org.MaxTags, org.Created, org.Revised)
|
||||
func (s Store) AddOrganization(ctx domain.RequestContext, o org.Organization) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, c_domain, c_email, c_anonaccess, c_serial, c_maxtags, c_sub, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||
o.RefID, o.Company, o.Title, o.Message, strings.ToLower(o.Domain),
|
||||
strings.ToLower(o.Email), o.AllowAnonymousAccess, o.Serial, o.MaxTags, o.Subscription, o.Created, o.Revised)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to execute insert for org")
|
||||
|
@ -49,8 +49,8 @@ func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Or
|
|||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, c_maxtags AS maxtags,
|
||||
c_created AS created, c_revised AS revised
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_refid=?`),
|
||||
id)
|
||||
|
@ -80,8 +80,8 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, c_maxtags AS maxtags,
|
||||
c_created AS created, c_revised AS revised
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_domain=? AND c_active=true`),
|
||||
subdomain)
|
||||
|
@ -95,8 +95,8 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, c_maxtags AS maxtags,
|
||||
c_created AS created, c_revised AS revised
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_domain='' AND c_active=true`))
|
||||
|
||||
|
@ -113,7 +113,7 @@ func (s Store) UpdateOrganization(ctx domain.RequestContext, org org.Organizatio
|
|||
|
||||
_, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET
|
||||
c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email,
|
||||
c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_revised=:revised
|
||||
c_anonaccess=:allowanonymousaccess, c_sub=:subscription, c_maxtags=:maxtags, c_revised=:revised
|
||||
WHERE c_refid=:refid`,
|
||||
&org)
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.add"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.update"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -510,7 +510,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.delete"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -608,7 +608,7 @@ func (h *Handler) DeletePages(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.delete.pages"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -721,7 +721,7 @@ func (h *Handler) ChangePageSequence(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.sequence"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -791,7 +791,7 @@ func (h *Handler) ChangePageLevel(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.level"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -987,7 +987,7 @@ func (h *Handler) GetDocumentRevisions(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.document.revisions"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -1018,7 +1018,7 @@ func (h *Handler) GetRevisions(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.revisions"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -1053,7 +1053,7 @@ func (h *Handler) GetDiff(w http.ResponseWriter, r *http.Request) {
|
|||
method := "page.diff"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func (h *Handler) DeleteUserPin(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ func (h *Handler) UpdatePinSequence(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
|
313
domain/product.go
Normal file
313
domain/product.go
Normal file
|
@ -0,0 +1,313 @@
|
|||
// 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
|
||||
|
||||
package domain
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"encoding/xml"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Edition is either Community or Enterprise.
|
||||
type Edition string
|
||||
|
||||
// Package controls feature-set within edition.
|
||||
type Package string
|
||||
|
||||
// Plan tells us if instance if self-hosted or Documize SaaS/Cloud.
|
||||
type Plan string
|
||||
|
||||
// Seats represents number of users.
|
||||
type Seats int
|
||||
|
||||
const (
|
||||
// CommunityEdition is AGPL licensed open core of product.
|
||||
CommunityEdition Edition = "Community"
|
||||
|
||||
// EnterpriseEdition is proprietary closed-source product.
|
||||
EnterpriseEdition Edition = "Enterprise"
|
||||
|
||||
// PackageEssentials provides core capabilities.
|
||||
PackageEssentials Package = "Essentials"
|
||||
|
||||
// PackageAdvanced provides analytics, reporting,
|
||||
// content lifecycle, content verisoning, and audit logs.
|
||||
PackageAdvanced Package = "Advanced"
|
||||
|
||||
// PackagePremium provides actions, feedback capture,
|
||||
// approvals workflow, secure external sharing.
|
||||
PackagePremium Package = "Premium"
|
||||
|
||||
// PackageDataCenter provides multi-tenanting
|
||||
// and a bunch of professional services.
|
||||
PackageDataCenter Package = "Data Center"
|
||||
|
||||
// PlanCloud represents *.documize.com hosting.
|
||||
PlanCloud Plan = "Cloud"
|
||||
|
||||
// PlanSelfHost represents privately hosted Documize instance.
|
||||
PlanSelfHost Plan = "Self-host"
|
||||
|
||||
// Seats0 is 0 users.
|
||||
Seats0 Seats = 0
|
||||
|
||||
// Seats1 is 10 users.
|
||||
Seats1 Seats = 10
|
||||
|
||||
// Seats2 is 25 users.
|
||||
Seats2 Seats = 25
|
||||
|
||||
//Seats3 is 50 users.
|
||||
Seats3 Seats = 50
|
||||
|
||||
// Seats4 is 100 users.
|
||||
Seats4 Seats = 100
|
||||
|
||||
//Seats5 is 250 users.
|
||||
Seats5 Seats = 250
|
||||
|
||||
// Seats6 is unlimited.
|
||||
Seats6 Seats = 9999
|
||||
)
|
||||
|
||||
// Product provides product meta information and handles
|
||||
// subscription validation for Enterprise edition.
|
||||
type Product struct {
|
||||
Edition Edition
|
||||
Title string
|
||||
Version string
|
||||
Major string
|
||||
Minor string
|
||||
Patch string
|
||||
Revision int
|
||||
|
||||
// UserCount is number of users within Documize instance by tenant.
|
||||
UserCount map[string]int
|
||||
}
|
||||
|
||||
// IsValid returns if subscription is valid using RequestContext.
|
||||
func (p *Product) IsValid(ctx RequestContext) bool {
|
||||
// Community edition is always valid.
|
||||
if p.Edition == CommunityEdition {
|
||||
return true
|
||||
}
|
||||
|
||||
// Empty means we cannot be valid.
|
||||
if ctx.Subscription.IsEmpty() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Enterprise edition is valid if system has loaded up user count by tenant.
|
||||
if uc, ok := p.UserCount[ctx.OrgID]; ok {
|
||||
// Enterprise edition is valid if subcription date is greater than now and we have enough users/seats.
|
||||
if time.Now().UTC().Before(ctx.Subscription.End) && uc <= int(ctx.Subscription.Seats) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SubscriptionData holds encrypted data and is unpacked into Subscription.
|
||||
type SubscriptionData struct {
|
||||
Key string `json:"key"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// SubscriptionXML represents subscription data as XML document.
|
||||
type SubscriptionXML struct {
|
||||
XMLName xml.Name `xml:"Documize"`
|
||||
Key string
|
||||
Signature string
|
||||
}
|
||||
|
||||
// Subscription data for customer.
|
||||
type Subscription struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Edition Edition `json:"edition"`
|
||||
Plan Plan `json:"plan"`
|
||||
Start time.Time `json:"start"`
|
||||
End time.Time `json:"end"`
|
||||
Seats Seats `json:"seats"`
|
||||
Trial bool `json:"trial"`
|
||||
Price uint64 `json:"price"`
|
||||
// Derived fields
|
||||
ActiveUsers int `json:"activeUsers"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// IsEmpty determines if we have a license.
|
||||
func (s *Subscription) IsEmpty() bool {
|
||||
return s.Seats == Seats0 &&
|
||||
len(s.Name) == 0 && len(s.Email) == 0 && s.Start.Year() == 1 && s.End.Year() == 1
|
||||
}
|
||||
|
||||
// SubscriptionUserAccount states number of active users by tenant.
|
||||
type SubscriptionUserAccount struct {
|
||||
OrgID string `json:"orgId"`
|
||||
Users int `json:"users"`
|
||||
}
|
||||
|
||||
// SubscriptionAsXML returns subscription data as XML document:
|
||||
//
|
||||
// <DocumizeLicense>
|
||||
// <Key>some key</Key>
|
||||
// <Signature>some signature</Signature>
|
||||
// </DocumizeLicense>
|
||||
//
|
||||
// XML document is empty in case of error.
|
||||
func SubscriptionAsXML(j SubscriptionData) (b []byte, err error) {
|
||||
x := &SubscriptionXML{Key: j.Key, Signature: j.Signature}
|
||||
b, err = xml.Marshal(x)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeSubscription returns Documize issued product licensing information.
|
||||
func DecodeSubscription(sd SubscriptionData) (sub Subscription, err error) {
|
||||
// Empty check.
|
||||
if len(sd.Key) == 0 || len(sd.Signature) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var ciphertext, signature []byte
|
||||
ciphertext, _ = hex.DecodeString(sd.Key)
|
||||
signature, _ = hex.DecodeString(sd.Signature)
|
||||
|
||||
// Load up keys.
|
||||
serverBlock, _ := pem.Decode([]byte(serverPublicKeyPEM4096))
|
||||
serverPublicKey, _ := x509.ParsePKIXPublicKey(serverBlock.Bytes)
|
||||
clientBlock, _ := pem.Decode([]byte(clientPrivateKeyPEM4096))
|
||||
clientPrivateKey, _ := x509.ParsePKCS1PrivateKey(clientBlock.Bytes)
|
||||
|
||||
label := []byte("dmzsub")
|
||||
hash := sha256.New()
|
||||
plainText, err := rsa.DecryptOAEP(hash, rand.Reader, clientPrivateKey, ciphertext, label)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// check signature
|
||||
var opts rsa.PSSOptions
|
||||
opts.SaltLength = rsa.PSSSaltLengthAuto
|
||||
PSSmessage := plainText
|
||||
newhash := crypto.SHA256
|
||||
pssh := newhash.New()
|
||||
pssh.Write(PSSmessage)
|
||||
hashed := pssh.Sum(nil)
|
||||
|
||||
err = rsa.VerifyPSS(serverPublicKey.(*rsa.PublicKey), newhash, hashed, signature, &opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(plainText, &sub)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var serverPublicKeyPEM4096 = `
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICITANBgkqhkiG9w0BAQEFAAOCAg4AMIICCQKCAgB1/J5crBk0rK+zkPn6p4nf
|
||||
qitsftN1/wrGq3xrXLhBax/+zyr3wm4Cd8bYANZjfzKw8jSoTqhoqwGF2J1A8Mjg
|
||||
Orfn04UGsM/Em+5g2b6d/Uc3tyoR7DJYwr0coc0rPZaypneAhaf6ob266CU8QEdE
|
||||
xkRkPMc/1TAOPmUkeuM2LI9Q/LDA5zPnN3WgYLGd7O1bSVOQYjw4KVp7Xr987Cec
|
||||
CHWzrrjwQ7vRYUqxpz1kQ8ZAmhnFAkAQzScE87kPKM9V2Txo0NQ9aL2idP2FoVi0
|
||||
obgGfJShI25YAeQncJBsyHV/uWxd3l/egaTHyQlcMgxBv61qsqgKzFZFsTNleQ3x
|
||||
SR4i8QnNLk0hwtR+NREJZRlIdMGBwV7elJa+8v8Zw6lbC1J8OghNseggGcBOoG6v
|
||||
OOwnEy6DK7hS3qfnHhFvR2zr9R5iQLHBIeVaVFZiLMKffRZnyHc3Dt5ozFMvpnzH
|
||||
TBaHzydI57mrYZKv3s8hEgVJqMA9d1zCd9bwPwDIqiR/tYgPadwagQwHE4d4Pg8f
|
||||
K8anfghelduKB0qIfeuQIEKmErEDK/qHj8HUC4nYUQy7hIo4F9D/HB22IfD6rM4D
|
||||
BrswLjnIDcW9ox8Sv2wT5FsRJqdYE80gmG68QjrGPcwqwkO6WhgAfr/LXx2kJ2HI
|
||||
BAMmkAYoyOaGK82XYKC14wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
var clientPublicKeyPEM4096 = `
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmtXoHjZ4Ky7gMqp9gY4f
|
||||
TQ+EhtgGxlkn3b48doQXhHemq/QyrcVj5FcHr9Um6pxop/HDQX2N7DEKX52ShFwa
|
||||
Ccv7iWWcZ8secope3nNouO80v9umb0LqWqVvfZSP4QbwDZa231baFWtnn2yiiOmA
|
||||
SkLmexLU+fmGht2Df9Q0gQLofGeE6YzLrdvnwa1NJHEiowgWaS5dsvsxoZV6zDXG
|
||||
428drRQ/JVt7soQbZENn0jiGSM+Tm77eXjMSu1oK8tnr7vm8ylBXj4rw6P4ONp50
|
||||
Dd+lERsdJFK5EaKN4xnWVVKayUlZTFE8ZAMXckF48dG8i9IgRkkEf7UcKekB/+hT
|
||||
1zIKHwmFjUy81jAmU5jySHFHfaGkIQKoKGFXQQt6st9rPLSLOFi8jLHYbJAO/Zs5
|
||||
DTaOoGLwDYcPMsgZswUyxySBUPDXDzg31sIJYl35GmZf6AX7vWvcX3C0NJxhnEFy
|
||||
eXnyJMe3yUHOJmJmYT91V/IKmUl51xdCdb8Gy9wM2oee9QEvM8BJEctGrXmcCuVb
|
||||
V7qkA79D3UK9QTbOthHsPWeWbaJDsmaxlwwp+crGTpcTLOyzwZdLaOr4bmNCQKUW
|
||||
OC0hPqiwhHsxPwA8Je98EvjLT9YC23+dCN2OoN4cpnRtl/rYNtlCHnIQ1l+n4hvs
|
||||
LMsDcJ/rlaak4OADM1YvNxUCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
var clientPrivateKeyPEM4096 = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
|
@ -14,13 +14,11 @@ package setting
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/core/event"
|
||||
"github.com/documize/community/core/request"
|
||||
"github.com/documize/community/core/response"
|
||||
"github.com/documize/community/core/streamutil"
|
||||
|
@ -128,96 +126,6 @@ func (h *Handler) SetSMTP(w http.ResponseWriter, r *http.Request) {
|
|||
response.WriteJSON(w, result)
|
||||
}
|
||||
|
||||
// License returns product license
|
||||
func (h *Handler) License(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !ctx.GlobalAdmin {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
config, _ := h.Store.Setting.Get("EDITION-LICENSE", "")
|
||||
if len(config) == 0 {
|
||||
config = "{}"
|
||||
}
|
||||
|
||||
x := &licenseXML{Key: "", Signature: ""}
|
||||
lj := licenseJSON{}
|
||||
|
||||
err := json.Unmarshal([]byte(config), &lj)
|
||||
if err == nil {
|
||||
x.Key = lj.Key
|
||||
x.Signature = lj.Signature
|
||||
} else {
|
||||
h.Runtime.Log.Error("failed to JSON unmarshal EDITION-LICENSE", err)
|
||||
}
|
||||
|
||||
output, err := xml.Marshal(x)
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("failed to XML marshal EDITION-LICENSE", err)
|
||||
}
|
||||
|
||||
response.WriteBytes(w, output)
|
||||
}
|
||||
|
||||
// SetLicense persists product license
|
||||
func (h *Handler) SetLicense(w http.ResponseWriter, r *http.Request) {
|
||||
method := "setting.SetLicense"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !ctx.GlobalAdmin {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
response.WriteBadRequestError(w, method, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var config string
|
||||
config = string(body)
|
||||
lj := licenseJSON{}
|
||||
x := licenseXML{Key: "", Signature: ""}
|
||||
|
||||
err1 := xml.Unmarshal([]byte(config), &x)
|
||||
if err1 == nil {
|
||||
lj.Key = x.Key
|
||||
lj.Signature = x.Signature
|
||||
} else {
|
||||
h.Runtime.Log.Error("failed to XML unmarshal EDITION-LICENSE", err)
|
||||
}
|
||||
|
||||
j, err2 := json.Marshal(lj)
|
||||
js := "{}"
|
||||
if err2 == nil {
|
||||
js = string(j)
|
||||
} else {
|
||||
h.Runtime.Log.Error("failed to JSON marshal EDITION-LICENSE", err2)
|
||||
}
|
||||
|
||||
h.Store.Setting.Set("EDITION-LICENSE", js)
|
||||
|
||||
/* ctx.Transaction, err = h.Runtime.Db.Beginx()*/
|
||||
//if err != nil {
|
||||
//response.WriteServerError(w, method, err)
|
||||
//return
|
||||
//}
|
||||
|
||||
/*ctx.Transaction.Commit()*/
|
||||
|
||||
h.Runtime.Log.Info("License changed")
|
||||
|
||||
event.Handler().Publish(string(event.TypeSystemLicenseChange))
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeSystemLicense)
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
||||
// AuthConfig returns installation-wide auth configuration
|
||||
func (h *Handler) AuthConfig(w http.ResponseWriter, r *http.Request) {
|
||||
method := "global.auth"
|
||||
|
|
|
@ -12,27 +12,7 @@
|
|||
// Package setting manages both global and user level settings
|
||||
package setting
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
type licenseXML struct {
|
||||
XMLName xml.Name `xml:"DocumizeLicense"`
|
||||
Key string
|
||||
Signature string
|
||||
}
|
||||
|
||||
type licenseJSON struct {
|
||||
Key string `json:"key"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type authData struct {
|
||||
AuthProvider string `json:"authProvider"`
|
||||
AuthConfig string `json:"authConfig"`
|
||||
}
|
||||
|
||||
/*
|
||||
<DocumizeLicense>
|
||||
<Key>some key</Key>
|
||||
<Signature>some signature</Signature>
|
||||
</DocumizeLicense>
|
||||
*/
|
||||
|
|
|
@ -57,7 +57,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
method := "space.add"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -582,7 +582,7 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
|
|||
method := "space.remove"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -675,7 +675,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
method := "space.delete"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/model/account"
|
||||
"github.com/documize/community/model/activity"
|
||||
|
@ -123,7 +122,7 @@ type UserStorer interface {
|
|||
UpdateUserPassword(ctx domain.RequestContext, userID, salt, password string) (err error)
|
||||
DeactiveUser(ctx domain.RequestContext, userID string) (err error)
|
||||
ForgotUserPassword(ctx domain.RequestContext, email, token string) (err error)
|
||||
CountActiveUsers() (c []env.LicenseUserAcount)
|
||||
CountActiveUsers() (c []domain.SubscriptionUserAccount)
|
||||
MatchUsers(ctx domain.RequestContext, text string, maxMatches int) (u []user.User, err error)
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
|
|||
method := "template.saved"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
|
|||
d.UserID = ctx.UserID
|
||||
d.Name = docTitle
|
||||
|
||||
if h.Runtime.Product.Edition == env.CommunityEdition {
|
||||
if h.Runtime.Product.Edition == domain.CommunityEdition {
|
||||
d.Lifecycle = workflow.LifecycleLive
|
||||
} else {
|
||||
d.Lifecycle = sp.Lifecycle
|
||||
|
|
|
@ -35,12 +35,8 @@ func startRuntime() (rt *env.Runtime, s *store.Store) {
|
|||
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
|
||||
rt.Product.Edition = "Test"
|
||||
rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)
|
||||
rt.Product.License = env.License{}
|
||||
rt.Product.License.Seats = 1
|
||||
rt.Product.License.Trial = false
|
||||
rt.Product.License.Edition = env.CommunityEdition
|
||||
|
||||
// parse settings from command line and environment
|
||||
// parse settings from command line and environment
|
||||
rt.Flags = env.ParseFlags()
|
||||
boot.InitRuntime(rt, s)
|
||||
|
||||
|
|
|
@ -51,10 +51,9 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
method := "user.Add"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.License.IsValid(ctx.OrgID) {
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
}
|
||||
|
||||
if !ctx.Administrator {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
|
@ -101,7 +100,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
requestedPassword := secrets.GenerateRandomPassword()
|
||||
userModel.Salt = secrets.GenerateSalt()
|
||||
userModel.Password = secrets.GeneratePassword(requestedPassword, userModel.Salt)
|
||||
userModel.LastVersion = ctx.AppVersion
|
||||
userModel.LastVersion = fmt.Sprintf("v%s", h.Runtime.Product.Version)
|
||||
|
||||
// only create account if not dupe
|
||||
addUser := true
|
||||
|
|
|
@ -14,7 +14,6 @@ package user
|
|||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/documize/community/core/env"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -313,7 +312,7 @@ func (s Store) ForgotUserPassword(ctx domain.RequestContext, email, token string
|
|||
}
|
||||
|
||||
// CountActiveUsers returns the number of active users in the system.
|
||||
func (s Store) CountActiveUsers() (c []env.LicenseUserAcount) {
|
||||
func (s Store) CountActiveUsers() (c []domain.SubscriptionUserAccount) {
|
||||
err := s.Runtime.Db.Select(&c, "SELECT c_orgid AS orgid, COUNT(*) AS users FROM dmz_user_account WHERE c_active=true GROUP BY c_orgid ORDER BY c_orgid")
|
||||
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue