diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/i18n.go | 140 | ||||
-rw-r--r-- | utils/license.go | 157 | ||||
-rw-r--r-- | utils/license_test.go | 50 |
3 files changed, 347 insertions, 0 deletions
diff --git a/utils/i18n.go b/utils/i18n.go new file mode 100644 index 000000000..4fc8c725a --- /dev/null +++ b/utils/i18n.go @@ -0,0 +1,140 @@ +package utils + +import ( + "io/ioutil" + "net/http" + "path/filepath" + "strings" + + l4g "github.com/alecthomas/log4go" + "github.com/cloudfoundry/jibber_jabber" + "github.com/mattermost/platform/model" + "github.com/nicksnyder/go-i18n/i18n" +) + +const ( + SESSION_LOCALE = "MMLOCALE" +) + +var T i18n.TranslateFunc +var locales map[string]string = make(map[string]string) + +func InitTranslations() { + i18nDirectory := FindDir("i18n") + files, _ := ioutil.ReadDir(i18nDirectory) + for _, f := range files { + if filepath.Ext(f.Name()) == ".json" { + filename := f.Name() + locales[strings.Split(filename, ".")[0]] = i18nDirectory + filename + i18n.MustLoadTranslationFile(i18nDirectory + filename) + } + } + + T = GetTranslationsBySystemLocale() +} + +func GetTranslationsBySystemLocale() i18n.TranslateFunc { + locale := model.DEFAULT_LOCALE + if userLanguage, err := jibber_jabber.DetectLanguage(); err == nil { + locale = userLanguage + } + + if locales[locale] == "" { + l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE) + + if locales[model.DEFAULT_LOCALE] == "" { + panic("Failed to load system translations for '" + model.DEFAULT_LOCALE + "'") + } + } + + translations, _ := i18n.Tfunc(locale) + if translations == nil { + panic("Failed to load system translations") + } + + l4g.Info(translations("utils.i18n.loaded"), locale, locales[locale]) + return translations +} + +func SetTranslations(locale string) i18n.TranslateFunc { + translations, _ := i18n.Tfunc(locale) + return translations +} + +func GetTranslations(w http.ResponseWriter, r *http.Request) i18n.TranslateFunc { + translations, _ := getTranslationsAndLocale(w, r) + return translations +} + +func GetTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) { + return getTranslationsAndLocale(w, r) +} + +func SetLocaleCookie(w http.ResponseWriter, lang string, sessionCacheInMinutes int) { + maxAge := (sessionCacheInMinutes * 60) + cookie := &http.Cookie{ + Name: SESSION_LOCALE, + Value: lang, + Path: "/", + MaxAge: maxAge, + } + + http.SetCookie(w, cookie) +} + +// var keyRegexp = regexp.MustCompile(`:[[:word:]]+`) +// func MaybeExpandNamedText(text string, args ...interface{}) string { +// var ( +// arg = args[0] +// argval = reflect.ValueOf(arg) +// ) +// if argval.Kind() == reflect.Ptr { +// argval = argval.Elem() +// } + +// if argval.Kind() == reflect.Map && argval.Type().Key().Kind() == reflect.String { +// return expandNamedText(text, func(key string) reflect.Value { +// return argval.MapIndex(reflect.ValueOf(key)) +// }) +// } +// if argval.Kind() != reflect.Struct { +// return text +// } + +// return expandNamedText(text, argval.FieldByName) +// } + +// func expandNamedText(text string, keyGetter func(key string) reflect.Value) string { +// return keyRegexp.ReplaceAllStringFunc(text, func(key string) string { +// val := keyGetter(key[1:]) +// if !val.IsValid() { +// return key +// } +// newVar, _ := val.Interface().(string) +// return newVar +// }) +// } + +func getTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) { + var translations i18n.TranslateFunc + var _ error + localeCookie := "" + if cookie, err := r.Cookie(SESSION_LOCALE); err == nil { + localeCookie = cookie.Value + if locales[localeCookie] != "" { + translations, _ = i18n.Tfunc(localeCookie) + return translations, localeCookie + } + } + + localeCookie = strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0] + if locales[localeCookie] != "" { + translations, _ = i18n.Tfunc(localeCookie) + SetLocaleCookie(w, localeCookie, 10) + return translations, localeCookie + } + + translations, _ = i18n.Tfunc(model.DEFAULT_LOCALE) + SetLocaleCookie(w, model.DEFAULT_LOCALE, 10) + return translations, model.DEFAULT_LOCALE +} diff --git a/utils/license.go b/utils/license.go new file mode 100644 index 000000000..7594e33af --- /dev/null +++ b/utils/license.go @@ -0,0 +1,157 @@ +// Copyright (c) 2015 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" + + l4g "github.com/alecthomas/log4go" + + "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) + +// test public key +var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3/k3Al9q1Xe+xngQ/yGn +0suaJopea3Cpf6NjIHdO8sYTwLlxqt0Mdb+qBR9LbCjZfcNmqc5mZONvsyCEoN/5 +VoLdlv1m9ao2BSAWphUxE2CPdUWdLOsDbQWliSc5//UhiYeR+67Xxon0Hg0LKXF6 +PumRIWQenRHJWqlUQZ147e7/1v9ySVRZksKpvlmMDzgq+kCH/uyM1uVP3z7YXhlN +K7vSSQYbt4cghvWQxDZFwpLlsChoY+mmzClgq+Yv6FLhj4/lk94twdOZau/AeZFJ +NxpC+5KFhU+xSeeklNqwCgnlOyZ7qSTxmdJHb+60SwuYnnGIYzLJhY4LYDr4J+KR +1wIDAQAB +-----END PUBLIC KEY-----`) + +func LoadLicense() { + file, err := os.Open(LicenseLocation()) + if err != nil { + l4g.Warn("Unable to open/find license file") + return + } + defer file.Close() + + buf := bytes.NewBuffer(nil) + io.Copy(buf, file) + + if success, licenseStr := ValidateLicense(buf.Bytes()); success { + license := model.LicenseFromJson(strings.NewReader(licenseStr)) + SetLicense(license) + } + + l4g.Warn("No valid enterprise license found") +} + +func SetLicense(license *model.License) bool { + license.Features.SetDefaults() + + if !license.IsExpired() && license.IsStarted() { + License = license + IsLicensed = true + ClientLicense = getClientLicense(license) + return true + } + + return false +} + +func LicenseLocation() string { + return filepath.Dir(CfgFileName) + "/" + LICENSE_FILENAME +} + +func RemoveLicense() bool { + License = &model.License{} + IsLicensed = false + ClientLicense = getClientLicense(License) + + if err := os.Remove(LicenseLocation()); err != nil { + l4g.Error("Unable to remove license file, err=%v", err.Error()) + return false + } + + return true +} + +func ValidateLicense(signed []byte) (bool, string) { + decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed))) + + _, err := base64.StdEncoding.Decode(decoded, signed) + if err != nil { + l4g.Error("Encountered error decoding license, err=%v", err.Error()) + return false, "" + } + + if len(decoded) <= 256 { + l4g.Error("Signed license not long enough") + return false, "" + } + + // remove null terminator + if decoded[len(decoded)-1] == byte(0) { + decoded = decoded[:len(decoded)-1] + } + + plaintext := decoded[:len(decoded)-256] + signature := decoded[len(decoded)-256:] + + block, _ := pem.Decode(publicKey) + + public, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + l4g.Error("Encountered error signing license, err=%v", err.Error()) + return false, "" + } + + rsaPublic := public.(*rsa.PublicKey) + + h := sha512.New() + h.Write(plaintext) + d := h.Sum(nil) + + err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature) + if err != nil { + l4g.Error("Invalid signature, err=%v", err.Error()) + return false, "" + } + + return true, string(plaintext) +} + +func getClientLicense(l *model.License) map[string]string { + props := make(map[string]string) + + props["IsLicensed"] = strconv.FormatBool(IsLicensed) + + if IsLicensed { + props["Users"] = strconv.Itoa(*l.Features.Users) + props["LDAP"] = strconv.FormatBool(*l.Features.LDAP) + props["GoogleSSO"] = strconv.FormatBool(*l.Features.GoogleSSO) + props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10) + props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10) + props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10) + props["Name"] = l.Customer.Name + props["Email"] = l.Customer.Email + props["Company"] = l.Customer.Company + props["PhoneNumber"] = l.Customer.PhoneNumber + } + + return props +} diff --git a/utils/license_test.go b/utils/license_test.go new file mode 100644 index 000000000..826107032 --- /dev/null +++ b/utils/license_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package utils + +import ( + "github.com/mattermost/platform/model" + "testing" +) + +func TestSetLicense(t *testing.T) { + l1 := &model.License{} + l1.Features = &model.Features{} + l1.Customer = &model.Customer{} + l1.StartsAt = model.GetMillis() - 1000 + l1.ExpiresAt = model.GetMillis() + 100000 + if ok := SetLicense(l1); !ok { + t.Fatal("license should have worked") + } + + l2 := &model.License{} + l2.Features = &model.Features{} + l2.Customer = &model.Customer{} + l2.StartsAt = model.GetMillis() - 1000 + l2.ExpiresAt = model.GetMillis() - 100 + if ok := SetLicense(l2); ok { + t.Fatal("license should have failed") + } + + l3 := &model.License{} + l3.Features = &model.Features{} + l3.Customer = &model.Customer{} + l3.StartsAt = model.GetMillis() + 10000 + l3.ExpiresAt = model.GetMillis() + 100000 + if ok := SetLicense(l3); ok { + t.Fatal("license should have failed") + } +} + +func TestValidateLicense(t *testing.T) { + b1 := []byte("junk") + if ok, _ := ValidateLicense(b1); ok { + t.Fatal("should have failed - bad license") + } + + b2 := []byte("junkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunk") + if ok, _ := ValidateLicense(b2); ok { + t.Fatal("should have failed - bad license") + } +} |