summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoramWilander <jwawilander@gmail.com>2016-02-04 13:00:03 -0500
committerJoramWilander <jwawilander@gmail.com>2016-02-04 13:35:44 -0500
commite45282deaa1d78d7ff3a125e9fd11e3fdc120b07 (patch)
treec116beae6abed1b703c481dfd63df36d689a8af5
parent7e8389cd0538fb6aff3931fb23714158d3f24449 (diff)
downloadchat-e45282deaa1d78d7ff3a125e9fd11e3fdc120b07.tar.gz
chat-e45282deaa1d78d7ff3a125e9fd11e3fdc120b07.tar.bz2
chat-e45282deaa1d78d7ff3a125e9fd11e3fdc120b07.zip
Move license storage to database
-rw-r--r--api/license.go32
-rw-r--r--i18n/en.json26
-rw-r--r--i18n/es.json6
-rw-r--r--mattermost.go22
-rw-r--r--model/license.go26
-rw-r--r--model/system.go1
-rw-r--r--store/sql_license_store.go83
-rw-r--r--store/sql_license_store_test.go43
-rw-r--r--store/sql_store.go8
-rw-r--r--store/sql_system_store.go26
-rw-r--r--store/sql_system_store_test.go16
-rw-r--r--store/store.go9
-rw-r--r--utils/license.go37
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx2
14 files changed, 284 insertions, 53 deletions
diff --git a/api/license.go b/api/license.go
index 4077c0e46..23e7946c8 100644
--- a/api/license.go
+++ b/api/license.go
@@ -81,9 +81,24 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if err := writeFileLocally(data, utils.LicenseLocation()); err != nil {
- c.LogAudit("failed - could not save license file")
- c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "path="+utils.LicenseLocation())
+ record := &model.LicenseRecord{}
+ record.Id = license.Id
+ record.Bytes = string(data)
+ rchan := Srv.Store.License().Save(record)
+
+ sysVar := &model.System{}
+ sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID
+ sysVar.Value = license.Id
+ schan := Srv.Store.System().SaveOrUpdate(sysVar)
+
+ if result := <-rchan; result.Err != nil {
+ c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error())
+ utils.RemoveLicense()
+ return
+ }
+
+ if result := <-schan; result.Err != nil {
+ c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "")
utils.RemoveLicense()
return
}
@@ -100,9 +115,14 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("")
- if ok := utils.RemoveLicense(); !ok {
- c.LogAudit("failed - could not remove license file")
- c.Err = model.NewLocAppError("removeLicense", "api.license.remove_license.remove.app_error", nil, "")
+ utils.RemoveLicense()
+
+ sysVar := &model.System{}
+ sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID
+ sysVar.Value = ""
+
+ if result := <-Srv.Store.System().Update(sysVar); result.Err != nil {
+ c.Err = model.NewLocAppError("removeLicense", "api.license.remove_license.update.app_error", nil, "")
return
}
diff --git a/i18n/en.json b/i18n/en.json
index 74a873204..ad84ec15c 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -612,6 +612,10 @@
"translation": "License did not save properly."
},
{
+ "id": "api.license.add_license.save_active.app_error",
+ "translation": "Active license ID did not save properly."
+ },
+ {
"id": "api.license.add_license.unique_users.app_error",
"translation": "This license only supports {{.Users}} users, when your system has {{.Count}} unique users. Unique users are counted distinctly by email address. You can see total user count under Site Reports -> View Statistics."
},
@@ -1732,6 +1736,10 @@
"translation": "Unable to get channels"
},
{
+ "id": "mattermost.load_license.find.warn",
+ "translation": "Unable to find active license"
+ },
+ {
"id": "mattermost.bulletin.subject",
"translation": "Mattermost Security Bulletin"
},
@@ -2916,6 +2924,18 @@
"translation": "We encountered an error updating the system property"
},
{
+ "id": "store.sql_license.save.app_error",
+ "translation": "We encountered an error saving the license"
+ },
+ {
+ "id": "store.sql_license.get.app_error",
+ "translation": "We encountered an error getting the license"
+ },
+ {
+ "id": "store.sql_license.get.missing.app_error",
+ "translation": "A license with that ID was not found"
+ },
+ {
"id": "store.sql_team.get.find.app_error",
"translation": "We couldn't find the existing team"
},
@@ -3204,10 +3224,6 @@
"translation": "No valid enterprise license found"
},
{
- "id": "utils.license.load_license.open_find.warn",
- "translation": "Unable to open/find license file"
- },
- {
"id": "utils.license.remove_license.unable.error",
"translation": "Unable to remove license file, err=%v"
},
@@ -3531,4 +3547,4 @@
"id": "web.watcher_fail.error",
"translation": "Failed to add directory to watcher %v"
}
-] \ No newline at end of file
+]
diff --git a/i18n/es.json b/i18n/es.json
index 2fe02b2b0..946cf424d 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -3204,10 +3204,6 @@
"translation": "No se encontrĂ³ una licencia enterprise vĂ¡lida"
},
{
- "id": "utils.license.load_license.open_find.warn",
- "translation": "No pudimos encontrar/abrir el achivo de licencia"
- },
- {
"id": "utils.license.remove_license.unable.error",
"translation": "No se pudo remover el archivo de la licencia, err=%v"
},
@@ -3531,4 +3527,4 @@
"id": "web.watcher_fail.error",
"translation": "Falla al agregar el directorio a ser vigilado %v"
}
-] \ No newline at end of file
+]
diff --git a/mattermost.go b/mattermost.go
index 43fa06601..5a18e2f40 100644
--- a/mattermost.go
+++ b/mattermost.go
@@ -69,7 +69,7 @@ func main() {
web.InitWeb()
if model.BuildEnterpriseReady == "true" {
- utils.LoadLicense()
+ loadLicense()
}
if flagRunCmds {
@@ -95,6 +95,26 @@ func main() {
}
}
+func loadLicense() {
+ licenseId := ""
+ if result := <-api.Srv.Store.System().Get(); result.Err == nil {
+ props := result.Data.(model.StringMap)
+ licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID]
+ }
+
+ if len(licenseId) != 26 {
+ l4g.Warn(utils.T("mattermost.load_license.find.warn"))
+ return
+ }
+
+ if result := <-api.Srv.Store.License().Get(licenseId); result.Err == nil {
+ record := result.Data.(*model.LicenseRecord)
+ utils.LoadLicense([]byte(record.Bytes))
+ } else {
+ l4g.Warn(utils.T("mattermost.load_license.find.warn"))
+ }
+}
+
func setDiagnosticId() {
if result := <-api.Srv.Store.System().Get(); result.Err == nil {
props := result.Data.(model.StringMap)
diff --git a/model/license.go b/model/license.go
index a271b46b7..ea66fef0d 100644
--- a/model/license.go
+++ b/model/license.go
@@ -8,6 +8,12 @@ import (
"io"
)
+type LicenseRecord struct {
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ Bytes string `json:"-"`
+}
+
type License struct {
Id string `json:"id"`
IssuedAt int64 `json:"issued_at"`
@@ -83,3 +89,23 @@ func LicenseFromJson(data io.Reader) *License {
return nil
}
}
+
+func (lr *LicenseRecord) IsValid() *AppError {
+ if len(lr.Id) != 26 {
+ return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.id.app_error", nil, "")
+ }
+
+ if lr.CreateAt == 0 {
+ return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "")
+ }
+
+ if len(lr.Bytes) == 0 || len(lr.Bytes) > 10000 {
+ return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "")
+ }
+
+ return nil
+}
+
+func (lr *LicenseRecord) PreSave() {
+ lr.CreateAt = GetMillis()
+}
diff --git a/model/system.go b/model/system.go
index 70db529d5..b387749f6 100644
--- a/model/system.go
+++ b/model/system.go
@@ -12,6 +12,7 @@ const (
SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
+ SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
)
type System struct {
diff --git a/store/sql_license_store.go b/store/sql_license_store.go
new file mode 100644
index 000000000..f5d67bc5d
--- /dev/null
+++ b/store/sql_license_store.go
@@ -0,0 +1,83 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package store
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type SqlLicenseStore struct {
+ *SqlStore
+}
+
+func NewSqlLicenseStore(sqlStore *SqlStore) LicenseStore {
+ ls := &SqlLicenseStore{sqlStore}
+
+ for _, db := range sqlStore.GetAllConns() {
+ table := db.AddTableWithName(model.LicenseRecord{}, "Licenses").SetKeys(false, "Id")
+ table.ColMap("Id").SetMaxSize(26)
+ table.ColMap("Bytes").SetMaxSize(10000)
+ }
+
+ return ls
+}
+
+func (ls SqlLicenseStore) UpgradeSchemaIfNeeded() {
+}
+
+func (ls SqlLicenseStore) CreateIndexesIfNotExists() {
+}
+
+func (ls SqlLicenseStore) Save(license *model.LicenseRecord) StoreChannel {
+
+ storeChannel := make(StoreChannel)
+
+ go func() {
+ result := StoreResult{}
+
+ license.PreSave()
+ if result.Err = license.IsValid(); result.Err != nil {
+ storeChannel <- result
+ close(storeChannel)
+ return
+ }
+
+ // Only insert if not exists
+ if err := ls.GetReplica().SelectOne(&model.LicenseRecord{}, "SELECT * FROM Licenses WHERE Id = :Id", map[string]interface{}{"Id": license.Id}); err != nil {
+ if err := ls.GetMaster().Insert(license); err != nil {
+ result.Err = model.NewLocAppError("SqlLicenseStore.Save", "store.sql_license.save.app_error", nil, "license_id="+license.Id+", "+err.Error())
+ } else {
+ result.Data = license
+ }
+ }
+
+ storeChannel <- result
+ close(storeChannel)
+ }()
+
+ return storeChannel
+}
+
+func (ls SqlLicenseStore) Get(id string) StoreChannel {
+
+ storeChannel := make(StoreChannel)
+
+ go func() {
+ result := StoreResult{}
+
+ if obj, err := ls.GetReplica().Get(model.LicenseRecord{}, id); err != nil {
+ result.Err = model.NewLocAppError("SqlLicenseStore.Get", "store.sql_license.get.app_error", nil, "license_id="+id+", "+err.Error())
+ } else if obj == nil {
+ result.Err = model.NewLocAppError("SqlLicenseStore.Get", "store.sql_license.get.missing.app_error", nil, "license_id="+id)
+ } else {
+ result.Data = obj.(*model.LicenseRecord)
+ }
+
+ storeChannel <- result
+ close(storeChannel)
+
+ }()
+
+ return storeChannel
+}
diff --git a/store/sql_license_store_test.go b/store/sql_license_store_test.go
new file mode 100644
index 000000000..ad24a6af7
--- /dev/null
+++ b/store/sql_license_store_test.go
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package store
+
+import (
+ "github.com/mattermost/platform/model"
+ "testing"
+)
+
+func TestLicenseStoreSave(t *testing.T) {
+ Setup()
+
+ l1 := model.LicenseRecord{}
+ l1.Id = model.NewId()
+ l1.Bytes = "junk"
+
+ if err := (<-store.License().Save(&l1)).Err; err != nil {
+ t.Fatal("couldn't save license record", err)
+ }
+
+ if err := (<-store.License().Save(&l1)).Err; err != nil {
+ t.Fatal("shouldn't fail on trying to save existing license record", err)
+ }
+}
+
+func TestLicenseStoreGet(t *testing.T) {
+ Setup()
+
+ l1 := model.LicenseRecord{}
+ l1.Id = model.NewId()
+ l1.Bytes = "junk"
+
+ Must(store.License().Save(&l1))
+
+ if r := <-store.License().Get(l1.Id); r.Err != nil {
+ t.Fatal("couldn't get license", r.Err)
+ } else {
+ if r.Data.(*model.LicenseRecord).Bytes != l1.Bytes {
+ t.Fatal("license bytes didn't match")
+ }
+ }
+}
diff --git a/store/sql_store.go b/store/sql_store.go
index 8517eb1a2..a994ec57e 100644
--- a/store/sql_store.go
+++ b/store/sql_store.go
@@ -49,6 +49,7 @@ type SqlStore struct {
webhook WebhookStore
command CommandStore
preference PreferenceStore
+ license LicenseStore
}
func NewSqlStore() Store {
@@ -103,6 +104,7 @@ func NewSqlStore() Store {
sqlStore.webhook = NewSqlWebhookStore(sqlStore)
sqlStore.command = NewSqlCommandStore(sqlStore)
sqlStore.preference = NewSqlPreferenceStore(sqlStore)
+ sqlStore.license = NewSqlLicenseStore(sqlStore)
err := sqlStore.master.CreateTablesIfNotExists()
if err != nil {
@@ -120,6 +122,7 @@ func NewSqlStore() Store {
sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded()
sqlStore.command.(*SqlCommandStore).UpgradeSchemaIfNeeded()
sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded()
+ sqlStore.license.(*SqlLicenseStore).UpgradeSchemaIfNeeded()
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
@@ -132,6 +135,7 @@ func NewSqlStore() Store {
sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
sqlStore.command.(*SqlCommandStore).CreateIndexesIfNotExists()
sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
+ sqlStore.license.(*SqlLicenseStore).CreateIndexesIfNotExists()
sqlStore.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
@@ -523,6 +527,10 @@ func (ss SqlStore) Preference() PreferenceStore {
return ss.preference
}
+func (ss SqlStore) License() LicenseStore {
+ return ss.license
+}
+
type mattermConverter struct{}
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
diff --git a/store/sql_system_store.go b/store/sql_system_store.go
index cfd4a670f..f8da06cec 100644
--- a/store/sql_system_store.go
+++ b/store/sql_system_store.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
@@ -47,6 +47,30 @@ func (s SqlSystemStore) Save(system *model.System) StoreChannel {
return storeChannel
}
+func (s SqlSystemStore) SaveOrUpdate(system *model.System) StoreChannel {
+
+ storeChannel := make(StoreChannel)
+
+ go func() {
+ result := StoreResult{}
+
+ if err := s.GetReplica().SelectOne(&model.System{}, "SELECT * FROM Systems WHERE Name = :Name", map[string]interface{}{"Name": system.Name}); err == nil {
+ if _, err := s.GetMaster().Update(system); err != nil {
+ result.Err = model.NewLocAppError("SqlSystemStore.SaveOrUpdate", "store.sql_system.update.app_error", nil, "")
+ }
+ } else {
+ if err := s.GetMaster().Insert(system); err != nil {
+ result.Err = model.NewLocAppError("SqlSystemStore.SaveOrUpdate", "store.sql_system.save.app_error", nil, "")
+ }
+ }
+
+ storeChannel <- result
+ close(storeChannel)
+ }()
+
+ return storeChannel
+}
+
func (s SqlSystemStore) Update(system *model.System) StoreChannel {
storeChannel := make(StoreChannel)
diff --git a/store/sql_system_store_test.go b/store/sql_system_store_test.go
index 8ff5445cc..ce149e97a 100644
--- a/store/sql_system_store_test.go
+++ b/store/sql_system_store_test.go
@@ -31,3 +31,19 @@ func TestSqlSystemStore(t *testing.T) {
t.Fatal()
}
}
+
+func TestSqlSystemStoreSaveOrUpdate(t *testing.T) {
+ Setup()
+
+ system := &model.System{Name: model.NewId(), Value: "value"}
+
+ if err := (<-store.System().SaveOrUpdate(system)).Err; err != nil {
+ t.Fatal(err)
+ }
+
+ system.Value = "value2"
+
+ if r := <-store.System().SaveOrUpdate(system); r.Err != nil {
+ t.Fatal(r.Err)
+ }
+}
diff --git a/store/store.go b/store/store.go
index b6b86e0d9..952b96e87 100644
--- a/store/store.go
+++ b/store/store.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
@@ -39,6 +39,7 @@ type Store interface {
Webhook() WebhookStore
Command() CommandStore
Preference() PreferenceStore
+ License() LicenseStore
MarkSystemRanUnitTests()
Close()
}
@@ -164,6 +165,7 @@ type OAuthStore interface {
type SystemStore interface {
Save(system *model.System) StoreChannel
+ SaveOrUpdate(system *model.System) StoreChannel
Update(system *model.System) StoreChannel
Get() StoreChannel
}
@@ -203,3 +205,8 @@ type PreferenceStore interface {
PermanentDeleteByUser(userId string) StoreChannel
IsFeatureEnabled(feature, userId string) StoreChannel
}
+
+type LicenseStore interface {
+ Save(license *model.LicenseRecord) StoreChannel
+ Get(id string) StoreChannel
+}
diff --git a/utils/license.go b/utils/license.go
index 0d1cd597c..b773a163e 100644
--- a/utils/license.go
+++ b/utils/license.go
@@ -1,19 +1,15 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package utils
import (
- "bytes"
"crypto"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/base64"
"encoding/pem"
- "io"
- "os"
- "path/filepath"
"strconv"
"strings"
@@ -22,10 +18,6 @@ import (
"github.com/mattermost/platform/model"
)
-const (
- LICENSE_FILENAME = "active.dat"
-)
-
var IsLicensed bool = false
var License *model.License = &model.License{}
var ClientLicense map[string]string = make(map[string]string)
@@ -41,18 +33,8 @@ NxpC+5KFhU+xSeeklNqwCgnlOyZ7qSTxmdJHb+60SwuYnnGIYzLJhY4LYDr4J+KR
1wIDAQAB
-----END PUBLIC KEY-----`)
-func LoadLicense() {
- file, err := os.Open(LicenseLocation())
- if err != nil {
- l4g.Warn(T("utils.license.load_license.open_find.warn"))
- return
- }
- defer file.Close()
-
- buf := bytes.NewBuffer(nil)
- io.Copy(buf, file)
-
- if success, licenseStr := ValidateLicense(buf.Bytes()); success {
+func LoadLicense(licenseBytes []byte) {
+ if success, licenseStr := ValidateLicense(licenseBytes); success {
license := model.LicenseFromJson(strings.NewReader(licenseStr))
SetLicense(license)
return
@@ -74,21 +56,10 @@ func SetLicense(license *model.License) bool {
return false
}
-func LicenseLocation() string {
- return filepath.Dir(CfgFileName) + "/" + LICENSE_FILENAME
-}
-
-func RemoveLicense() bool {
+func RemoveLicense() {
License = &model.License{}
IsLicensed = false
ClientLicense = getClientLicense(License)
-
- if err := os.Remove(LicenseLocation()); err != nil {
- l4g.Error(T("utils.license.remove_license.unable.error"), err.Error())
- return false
- }
-
- return true
}
func ValidateLicense(signed []byte) (bool, string) {
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
index 7d74013f2..eadd8d412 100644
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -454,6 +454,7 @@ export default class AdminSidebar extends React.Component {
</ul>
<ul className='nav nav__sub-menu padded'>
{licenseSettings}
+ {audits}
<li>
<a
href='#'
@@ -466,7 +467,6 @@ export default class AdminSidebar extends React.Component {
/>
</a>
</li>
- {audits}
</ul>
</li>
</ul>