/store/sql_store.go
Go | 761 lines | 610 code | 131 blank | 20 comment | 160 complexity | 9d29ce7dfbebf059f9c2bfb8dfdf0f08 MD5 | raw file
- // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
- // See License.txt for license information.
- package store
- import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/hmac"
- crand "crypto/rand"
- "crypto/sha256"
- "crypto/sha512"
- dbsql "database/sql"
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- l4g "github.com/alecthomas/log4go"
- "io"
- sqltrace "log"
- "math/rand"
- "os"
- "strings"
- "time"
- "github.com/go-gorp/gorp"
- _ "github.com/go-sql-driver/mysql"
- _ "github.com/lib/pq"
- "github.com/mattermost/platform/model"
- "github.com/mattermost/platform/utils"
- )
- const (
- INDEX_TYPE_FULL_TEXT = "full_text"
- INDEX_TYPE_DEFAULT = "default"
- )
- type SqlStore struct {
- master *gorp.DbMap
- replicas []*gorp.DbMap
- team TeamStore
- channel ChannelStore
- post PostStore
- user UserStore
- audit AuditStore
- session SessionStore
- oauth OAuthStore
- system SystemStore
- webhook WebhookStore
- command CommandStore
- preference PreferenceStore
- license LicenseStore
- }
- func NewSqlStore() Store {
- sqlStore := &SqlStore{}
- sqlStore.master = setupConnection("master", utils.Cfg.SqlSettings.DriverName,
- utils.Cfg.SqlSettings.DataSource, utils.Cfg.SqlSettings.MaxIdleConns,
- utils.Cfg.SqlSettings.MaxOpenConns, utils.Cfg.SqlSettings.Trace)
- if len(utils.Cfg.SqlSettings.DataSourceReplicas) == 0 {
- sqlStore.replicas = make([]*gorp.DbMap, 1)
- sqlStore.replicas[0] = setupConnection(fmt.Sprintf("replica-%v", 0), utils.Cfg.SqlSettings.DriverName, utils.Cfg.SqlSettings.DataSource,
- utils.Cfg.SqlSettings.MaxIdleConns, utils.Cfg.SqlSettings.MaxOpenConns,
- utils.Cfg.SqlSettings.Trace)
- } else {
- sqlStore.replicas = make([]*gorp.DbMap, len(utils.Cfg.SqlSettings.DataSourceReplicas))
- for i, replica := range utils.Cfg.SqlSettings.DataSourceReplicas {
- sqlStore.replicas[i] = setupConnection(fmt.Sprintf("replica-%v", i), utils.Cfg.SqlSettings.DriverName, replica,
- utils.Cfg.SqlSettings.MaxIdleConns, utils.Cfg.SqlSettings.MaxOpenConns,
- utils.Cfg.SqlSettings.Trace)
- }
- }
- schemaVersion := sqlStore.GetCurrentSchemaVersion()
- // If the version is already set then we are potentially in an 'upgrade needed' state
- if schemaVersion != "" {
- // Check to see if it's the most current database schema version
- if !model.IsCurrentVersion(schemaVersion) {
- // If we are upgrading from the previous version then print a warning and continue
- if model.IsPreviousVersionsSupported(schemaVersion) {
- l4g.Warn(utils.T("store.sql.schema_out_of_date.warn"), schemaVersion)
- l4g.Warn(utils.T("store.sql.schema_upgrade_attempt.warn"), model.CurrentVersion)
- } else {
- // If this is an 'upgrade needed' state but the user is attempting to skip a version then halt the world
- l4g.Critical(utils.T("store.sql.schema_version.critical"), schemaVersion)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.schema_version.critical"), schemaVersion))
- }
- }
- }
- sqlStore.team = NewSqlTeamStore(sqlStore)
- sqlStore.channel = NewSqlChannelStore(sqlStore)
- sqlStore.post = NewSqlPostStore(sqlStore)
- sqlStore.user = NewSqlUserStore(sqlStore)
- sqlStore.audit = NewSqlAuditStore(sqlStore)
- sqlStore.session = NewSqlSessionStore(sqlStore)
- sqlStore.oauth = NewSqlOAuthStore(sqlStore)
- sqlStore.system = NewSqlSystemStore(sqlStore)
- sqlStore.webhook = NewSqlWebhookStore(sqlStore)
- sqlStore.command = NewSqlCommandStore(sqlStore)
- sqlStore.preference = NewSqlPreferenceStore(sqlStore)
- sqlStore.license = NewSqlLicenseStore(sqlStore)
- err := sqlStore.master.CreateTablesIfNotExists()
- if err != nil {
- l4g.Critical(utils.T("store.sql.creating_tables.critical"), err)
- }
- sqlStore.team.(*SqlTeamStore).UpgradeSchemaIfNeeded()
- sqlStore.channel.(*SqlChannelStore).UpgradeSchemaIfNeeded()
- sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
- sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
- sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
- sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
- sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
- sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
- sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded()
- sqlStore.command.(*SqlCommandStore).UpgradeSchemaIfNeeded()
- sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded()
- sqlStore.license.(*SqlLicenseStore).UpgradeSchemaIfNeeded()
- sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
- sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
- sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
- sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
- sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
- sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
- sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
- sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
- sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
- sqlStore.command.(*SqlCommandStore).CreateIndexesIfNotExists()
- sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
- sqlStore.license.(*SqlLicenseStore).CreateIndexesIfNotExists()
- sqlStore.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
- if model.IsPreviousVersionsSupported(schemaVersion) && !model.IsCurrentVersion(schemaVersion) {
- sqlStore.system.Update(&model.System{Name: "Version", Value: model.CurrentVersion})
- l4g.Warn(utils.T("store.sql.upgraded.warn"), model.CurrentVersion)
- }
- if schemaVersion == "" {
- sqlStore.system.Save(&model.System{Name: "Version", Value: model.CurrentVersion})
- l4g.Info(utils.T("store.sql.schema_set.info"), model.CurrentVersion)
- }
- return sqlStore
- }
- func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap {
- db, err := dbsql.Open(driver, dataSource)
- if err != nil {
- l4g.Critical(utils.T("store.sql.open_conn.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.open_conn.critical"), err.Error()))
- }
- l4g.Info(utils.T("store.sql.pinging.info"), con_type)
- err = db.Ping()
- if err != nil {
- l4g.Critical(utils.T("store.sql.ping.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.open_conn.panic"), err.Error()))
- }
- db.SetMaxIdleConns(maxIdle)
- db.SetMaxOpenConns(maxOpen)
- var dbmap *gorp.DbMap
- if driver == "sqlite3" {
- dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}}
- } else if driver == model.DATABASE_DRIVER_MYSQL {
- dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}}
- } else if driver == model.DATABASE_DRIVER_POSTGRES {
- dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}}
- } else {
- l4g.Critical(utils.T("store.sql.dialect_driver.critical"))
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.dialect_driver.panic"), err.Error()))
- }
- if trace {
- dbmap.TraceOn("", sqltrace.New(os.Stdout, "sql-trace:", sqltrace.Lmicroseconds))
- }
- return dbmap
- }
- func (ss SqlStore) GetCurrentSchemaVersion() string {
- version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'")
- return version
- }
- func (ss SqlStore) MarkSystemRanUnitTests() {
- if result := <-ss.System().Get(); result.Err == nil {
- props := result.Data.(model.StringMap)
- unitTests := props[model.SYSTEM_RAN_UNIT_TESTS]
- if len(unitTests) == 0 {
- systemTests := &model.System{Name: model.SYSTEM_RAN_UNIT_TESTS, Value: "1"}
- <-ss.System().Save(systemTests)
- }
- }
- }
- func (ss SqlStore) DoesTableExist(tableName string) bool {
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- count, err := ss.GetMaster().SelectInt(
- `SELECT count(relname) FROM pg_class WHERE relname=$1`,
- strings.ToLower(tableName),
- )
- if err != nil {
- l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.table_exists.critical"), err.Error()))
- }
- return count > 0
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- count, err := ss.GetMaster().SelectInt(
- `SELECT
- COUNT(0) AS table_exists
- FROM
- information_schema.TABLES
- WHERE
- TABLE_SCHEMA = DATABASE()
- AND TABLE_NAME = ?
- `,
- tableName,
- )
- if err != nil {
- l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.table_exists.critical"), err.Error()))
- }
- return count > 0
- } else {
- l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
- time.Sleep(time.Second)
- panic(utils.T("store.sql.column_exists_missing_driver.critical"))
- }
- }
- func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool {
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- count, err := ss.GetMaster().SelectInt(
- `SELECT COUNT(0)
- FROM pg_attribute
- WHERE attrelid = $1::regclass
- AND attname = $2
- AND NOT attisdropped`,
- strings.ToLower(tableName),
- strings.ToLower(columnName),
- )
- if err != nil {
- if err.Error() == "pq: relation \""+strings.ToLower(tableName)+"\" does not exist" {
- return false
- }
- l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.column_exists.critical"), err.Error()))
- }
- return count > 0
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- count, err := ss.GetMaster().SelectInt(
- `SELECT
- COUNT(0) AS column_exists
- FROM
- information_schema.COLUMNS
- WHERE
- TABLE_SCHEMA = DATABASE()
- AND TABLE_NAME = ?
- AND COLUMN_NAME = ?`,
- tableName,
- columnName,
- )
- if err != nil {
- l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.column_exists.critical"), err.Error()))
- }
- return count > 0
- } else {
- l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
- time.Sleep(time.Second)
- panic(utils.T("store.sql.column_exists_missing_driver.critical"))
- }
- }
- func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool {
- if ss.DoesColumnExist(tableName, columnName) {
- return false
- }
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'")
- if err != nil {
- l4g.Critical(utils.T("store.sql.create_column.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.create_column.critical"), err.Error()))
- }
- return true
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'")
- if err != nil {
- l4g.Critical(utils.T("store.sql.create_column.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.create_column.critical"), err.Error()))
- }
- return true
- } else {
- l4g.Critical(utils.T("store.sql.create_column_missing_driver.critical"))
- time.Sleep(time.Second)
- panic(utils.T("store.sql.create_column_missing_driver.critical"))
- }
- }
- func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
- if !ss.DoesColumnExist(tableName, columnName) {
- return false
- }
- _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
- if err != nil {
- l4g.Critical(utils.T("store.sql.drop_column.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.drop_column.critical"), err.Error()))
- }
- return true
- }
- func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
- if !ss.DoesColumnExist(tableName, oldColumnName) {
- return false
- }
- var err error
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName)
- }
- if err != nil {
- l4g.Critical(utils.T("store.sql.rename_column.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.rename_column.critical"), err.Error()))
- }
- return true
- }
- func (ss SqlStore) GetMaxLengthOfColumnIfExists(tableName string, columnName string) string {
- if !ss.DoesColumnExist(tableName, columnName) {
- return ""
- }
- var result string
- var err error
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- result, err = ss.GetMaster().SelectStr("SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE table_name = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "'")
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- result, err = ss.GetMaster().SelectStr("SELECT character_maximum_length FROM information_schema.columns WHERE table_name = '" + strings.ToLower(tableName) + "' AND column_name = '" + strings.ToLower(columnName) + "'")
- }
- if err != nil {
- l4g.Critical(utils.T("store.sql.maxlength_column.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.maxlength_column.critical"), err.Error()))
- }
- return result
- }
- func (ss SqlStore) AlterColumnTypeIfExists(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
- if !ss.DoesColumnExist(tableName, columnName) {
- return false
- }
- var err error
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " MODIFY " + columnName + " " + mySqlColType)
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- _, err = ss.GetMaster().Exec("ALTER TABLE " + strings.ToLower(tableName) + " ALTER COLUMN " + strings.ToLower(columnName) + " TYPE " + postgresColType)
- }
- if err != nil {
- l4g.Critical(utils.T("store.sql.alter_column_type.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.alter_column_type.critical"), err.Error()))
- }
- return true
- }
- func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) {
- ss.createIndexIfNotExists(indexName, tableName, columnName, INDEX_TYPE_DEFAULT)
- }
- func (ss SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) {
- ss.createIndexIfNotExists(indexName, tableName, columnName, INDEX_TYPE_FULL_TEXT)
- }
- func (ss SqlStore) createIndexIfNotExists(indexName string, tableName string, columnName string, indexType string) {
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- _, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
- // It should fail if the index does not exist
- if err == nil {
- return
- }
- query := ""
- if indexType == INDEX_TYPE_FULL_TEXT {
- query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + columnName + "))"
- } else {
- query = "CREATE INDEX " + indexName + " ON " + tableName + " (" + columnName + ")"
- }
- _, err = ss.GetMaster().Exec(query)
- if err != nil {
- l4g.Critical(utils.T("store.sql.create_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.create_index.critical"), err.Error()))
- }
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
- if err != nil {
- l4g.Critical(utils.T("store.sql.check_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.check_index.critical"), err.Error()))
- }
- if count > 0 {
- return
- }
- fullTextIndex := ""
- if indexType == INDEX_TYPE_FULL_TEXT {
- fullTextIndex = " FULLTEXT "
- }
- _, err = ss.GetMaster().Exec("CREATE " + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + columnName + ")")
- if err != nil {
- l4g.Critical(utils.T("store.sql.create_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.create_index.critical"), err.Error()))
- }
- } else {
- l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
- time.Sleep(time.Second)
- panic(utils.T("store.sql.create_index_missing_driver.critical"))
- }
- }
- func (ss SqlStore) RemoveIndexIfExists(indexName string, tableName string) {
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- _, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
- // It should fail if the index does not exist
- if err == nil {
- return
- }
- _, err = ss.GetMaster().Exec("DROP INDEX " + indexName)
- if err != nil {
- l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.remove_index.critical"), err.Error()))
- }
- } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
- count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
- if err != nil {
- l4g.Critical(utils.T("store.sql.check_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.check_index.critical"), err.Error()))
- }
- if count > 0 {
- return
- }
- _, err = ss.GetMaster().Exec("DROP INDEX " + indexName + " ON " + tableName)
- if err != nil {
- l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
- time.Sleep(time.Second)
- panic(fmt.Sprintf(utils.T("store.sql.remove_index.critical"), err.Error()))
- }
- } else {
- l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
- time.Sleep(time.Second)
- panic(utils.T("store.sql.create_index_missing_driver.critical"))
- }
- }
- func IsUniqueConstraintError(err string, mysql string, postgres string) bool {
- unique := strings.Contains(err, "unique constraint") || strings.Contains(err, "Duplicate entry")
- field := strings.Contains(err, mysql) || strings.Contains(err, postgres)
- return unique && field
- }
- // func (ss SqlStore) GetColumnDataType(tableName, columnName string) string {
- // dataType, err := ss.GetMaster().SelectStr("SELECT data_type FROM INFORMATION_SCHEMA.COLUMNS where table_name = :Tablename AND column_name = :Columnname", map[string]interface{}{
- // "Tablename": tableName,
- // "Columnname": columnName,
- // })
- // if err != nil {
- // l4g.Critical(utils.T("store.sql.table_column_type.critical"), columnName, tableName, err.Error())
- // time.Sleep(time.Second)
- // panic(fmt.Sprintf(utils.T("store.sql.table_column_type.critical"), columnName, tableName, err.Error()))
- // }
- // return dataType
- // }
- func (ss SqlStore) GetMaster() *gorp.DbMap {
- return ss.master
- }
- func (ss SqlStore) GetReplica() *gorp.DbMap {
- return ss.replicas[rand.Intn(len(ss.replicas))]
- }
- func (ss SqlStore) GetAllConns() []*gorp.DbMap {
- all := make([]*gorp.DbMap, len(ss.replicas)+1)
- copy(all, ss.replicas)
- all[len(ss.replicas)] = ss.master
- return all
- }
- func (ss SqlStore) Close() {
- l4g.Info(utils.T("store.sql.closing.info"))
- ss.master.Db.Close()
- for _, replica := range ss.replicas {
- replica.Db.Close()
- }
- }
- func (ss SqlStore) Team() TeamStore {
- return ss.team
- }
- func (ss SqlStore) Channel() ChannelStore {
- return ss.channel
- }
- func (ss SqlStore) Post() PostStore {
- return ss.post
- }
- func (ss SqlStore) User() UserStore {
- return ss.user
- }
- func (ss SqlStore) Session() SessionStore {
- return ss.session
- }
- func (ss SqlStore) Audit() AuditStore {
- return ss.audit
- }
- func (ss SqlStore) OAuth() OAuthStore {
- return ss.oauth
- }
- func (ss SqlStore) System() SystemStore {
- return ss.system
- }
- func (ss SqlStore) Webhook() WebhookStore {
- return ss.webhook
- }
- func (ss SqlStore) Command() CommandStore {
- return ss.command
- }
- 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) {
- switch t := val.(type) {
- case model.StringMap:
- return model.MapToJson(t), nil
- case model.StringArray:
- return model.ArrayToJson(t), nil
- case model.EncryptStringMap:
- return encrypt([]byte(utils.Cfg.SqlSettings.AtRestEncryptKey), model.MapToJson(t))
- case model.StringInterface:
- return model.StringInterfaceToJson(t), nil
- }
- return val, nil
- }
- func (me mattermConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
- switch target.(type) {
- case *model.StringMap:
- binder := func(holder, target interface{}) error {
- s, ok := holder.(*string)
- if !ok {
- return errors.New(utils.T("store.sql.convert_string_map"))
- }
- b := []byte(*s)
- return json.Unmarshal(b, target)
- }
- return gorp.CustomScanner{new(string), target, binder}, true
- case *model.StringArray:
- binder := func(holder, target interface{}) error {
- s, ok := holder.(*string)
- if !ok {
- return errors.New(utils.T("store.sql.convert_string_array"))
- }
- b := []byte(*s)
- return json.Unmarshal(b, target)
- }
- return gorp.CustomScanner{new(string), target, binder}, true
- case *model.EncryptStringMap:
- binder := func(holder, target interface{}) error {
- s, ok := holder.(*string)
- if !ok {
- return errors.New(utils.T("store.sql.convert_encrypt_string_map"))
- }
- ue, err := decrypt([]byte(utils.Cfg.SqlSettings.AtRestEncryptKey), *s)
- if err != nil {
- return err
- }
- b := []byte(ue)
- return json.Unmarshal(b, target)
- }
- return gorp.CustomScanner{new(string), target, binder}, true
- case *model.StringInterface:
- binder := func(holder, target interface{}) error {
- s, ok := holder.(*string)
- if !ok {
- return errors.New(utils.T("store.sql.convert_string_interface"))
- }
- b := []byte(*s)
- return json.Unmarshal(b, target)
- }
- return gorp.CustomScanner{new(string), target, binder}, true
- }
- return gorp.CustomScanner{}, false
- }
- func encrypt(key []byte, text string) (string, error) {
- if text == "" || text == "{}" {
- return "", nil
- }
- plaintext := []byte(text)
- skey := sha512.Sum512(key)
- ekey, akey := skey[:32], skey[32:]
- block, err := aes.NewCipher(ekey)
- if err != nil {
- return "", err
- }
- macfn := hmac.New(sha256.New, akey)
- ciphertext := make([]byte, aes.BlockSize+macfn.Size()+len(plaintext))
- iv := ciphertext[:aes.BlockSize]
- if _, err := io.ReadFull(crand.Reader, iv); err != nil {
- return "", err
- }
- stream := cipher.NewCFBEncrypter(block, iv)
- stream.XORKeyStream(ciphertext[aes.BlockSize+macfn.Size():], plaintext)
- macfn.Write(ciphertext[aes.BlockSize+macfn.Size():])
- mac := macfn.Sum(nil)
- copy(ciphertext[aes.BlockSize:aes.BlockSize+macfn.Size()], mac)
- return base64.URLEncoding.EncodeToString(ciphertext), nil
- }
- func decrypt(key []byte, cryptoText string) (string, error) {
- if cryptoText == "" || cryptoText == "{}" {
- return "{}", nil
- }
- ciphertext, err := base64.URLEncoding.DecodeString(cryptoText)
- if err != nil {
- return "", err
- }
- skey := sha512.Sum512(key)
- ekey, akey := skey[:32], skey[32:]
- macfn := hmac.New(sha256.New, akey)
- if len(ciphertext) < aes.BlockSize+macfn.Size() {
- return "", errors.New(utils.T("store.sql.short_ciphertext"))
- }
- macfn.Write(ciphertext[aes.BlockSize+macfn.Size():])
- expectedMac := macfn.Sum(nil)
- mac := ciphertext[aes.BlockSize : aes.BlockSize+macfn.Size()]
- if hmac.Equal(expectedMac, mac) != true {
- return "", errors.New(utils.T("store.sql.incorrect_mac"))
- }
- block, err := aes.NewCipher(ekey)
- if err != nil {
- return "", err
- }
- if len(ciphertext) < aes.BlockSize {
- return "", errors.New(utils.T("store.sql.too_short_ciphertext"))
- }
- iv := ciphertext[:aes.BlockSize]
- ciphertext = ciphertext[aes.BlockSize+macfn.Size():]
- stream := cipher.NewCFBDecrypter(block, iv)
- stream.XORKeyStream(ciphertext, ciphertext)
- return fmt.Sprintf("%s", ciphertext), nil
- }