From 88e5a71e8c93b495cedaa07931a4f8052d9f12ed Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Tue, 22 Sep 2015 12:12:50 -0700 Subject: [PATCH] Adding service settings to admin console --- api/admin.go | 4 +- api/api_test.go | 2 +- api/channel_test.go | 2 +- api/command.go | 4 +- api/context.go | 38 +-- api/file.go | 4 +- api/file_benchmark_test.go | 2 +- api/file_test.go | 2 +- api/server.go | 4 +- api/team.go | 21 +- api/team_test.go | 2 +- api/user.go | 14 +- api/user_test.go | 4 +- api/web_socket_test.go | 2 +- config/config.json | 28 +- docker/dev/config_docker.json | 125 ++++----- docker/local/config_docker.json | 125 ++++----- manualtesting/manual_testing.go | 2 +- mattermost.go | 2 +- model/client.go | 1 + model/config.go | 26 +- utils/config.go | 13 +- .../admin_console/admin_controller.jsx | 6 +- .../admin_console/admin_sidebar.jsx | 9 + .../admin_console/email_settings.jsx | 98 ++++++- .../admin_console/image_settings.jsx | 45 +++ .../components/admin_console/log_settings.jsx | 24 +- .../admin_console/service_settings.jsx | 262 ++++++++++++++++++ .../components/admin_console/sql_settings.jsx | 2 +- web/react/components/login.jsx | 4 +- web/react/components/signup_team.jsx | 8 +- web/react/components/signup_user_complete.jsx | 4 +- .../components/team_signup_choose_auth.jsx | 4 +- web/react/utils/client.jsx | 2 - web/templates/head.html | 23 -- web/web.go | 8 +- web/web_test.go | 2 +- 37 files changed, 631 insertions(+), 297 deletions(-) create mode 100644 web/react/components/admin_console/service_settings.jsx diff --git a/api/admin.go b/api/admin.go index ca66b7cb40..568d8f6e87 100644 --- a/api/admin.go +++ b/api/admin.go @@ -35,7 +35,7 @@ func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { var lines []string - if utils.Cfg.LogSettings.FileEnable { + if utils.Cfg.LogSettings.EnableFile { file, err := os.Open(utils.GetLogFileLocation(utils.Cfg.LogSettings.FileLocation)) if err != nil { @@ -82,7 +82,7 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { return } - if len(cfg.ServiceSettings.Port) == 0 { + if len(cfg.ServiceSettings.ListenAddress) == 0 { c.SetInvalidParam("saveConfig", "config") return } diff --git a/api/api_test.go b/api/api_test.go index 490f8ab5b5..761f3e33f6 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -18,7 +18,7 @@ func Setup() { NewServer() StartServer() InitApi() - Client = model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port) + Client = model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) } } diff --git a/api/channel_test.go b/api/channel_test.go index 14bfe1cf79..7845ac499a 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -627,7 +627,7 @@ func TestGetChannelExtraInfo(t *testing.T) { currentEtag = cache_result.Etag } - Client2 := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port) + Client2 := model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "tester2@test.com", Nickname: "Tester 2", Password: "pwd"} user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User) diff --git a/api/command.go b/api/command.go index bc55f206bb..0d2f7597b5 100644 --- a/api/command.go +++ b/api/command.go @@ -215,8 +215,8 @@ func joinCommand(c *Context, command *model.Command) bool { func loadTestCommand(c *Context, command *model.Command) bool { cmd := "/loadtest" - // This command is only available when AllowTesting is true - if !utils.Cfg.ServiceSettings.AllowTesting { + // This command is only available when EnableTesting is true + if !utils.Cfg.ServiceSettings.EnableTesting { return false } diff --git a/api/context.go b/api/context.go index c4684221d7..9a276a1a18 100644 --- a/api/context.go +++ b/api/context.go @@ -107,21 +107,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { isTokenFromQueryString = true } - protocol := "http" - - // If the request came from the ELB then assume this is produciton - // and redirect all http requests to https - if utils.Cfg.ServiceSettings.UseSSL { - forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO) - if forwardProto == "http" { - l4g.Info("redirecting http request to https for %v", r.URL.Path) - http.Redirect(w, r, "https://"+r.Host, http.StatusTemporaryRedirect) - return - } else { - protocol = "https" - } - } - + protocol := GetProtocol(r) c.setSiteURL(protocol + "://" + r.Host) w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) @@ -209,6 +195,14 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +func GetProtocol(r *http.Request) string { + if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" { + return "https" + } else { + return "http" + } +} + func (c *Context) LogAudit(extraInfo string) { audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} if r := <-Srv.Store.Audit().Save(audit); r.Err != nil { @@ -385,6 +379,11 @@ func (c *Context) GetSiteURL() string { func GetIpAddress(r *http.Request) string { address := r.Header.Get(model.HEADER_FORWARDED) + + if len(address) == 0 { + address = r.Header.Get(model.HEADER_REAL_IP) + } + if len(address) == 0 { address, _, _ = net.SplitHostPort(r.RemoteAddr) } @@ -458,14 +457,7 @@ func IsPrivateIpAddress(ipAddress string) bool { func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) { - protocol := "http" - if utils.Cfg.ServiceSettings.UseSSL { - forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO) - if forwardProto != "http" { - protocol = "https" - } - } - + protocol := GetProtocol(r) SiteURL := protocol + "://" + r.Host m := make(map[string]string) diff --git a/api/file.go b/api/file.go index 61d0df413f..c85d241f3f 100644 --- a/api/file.go +++ b/api/file.go @@ -399,7 +399,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { asyncGetFile(path, fileData) if len(hash) > 0 && len(data) > 0 && len(teamId) == 26 { - if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) { + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ImageSettings.PublicLinkSalt)) { c.Err = model.NewAppError("getFile", "The public link does not appear to be valid", "") return } @@ -477,7 +477,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { newProps["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(newProps) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ImageSettings.PublicLinkSalt)) url := fmt.Sprintf("%s/api/v1/files/get/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId) diff --git a/api/file_benchmark_test.go b/api/file_benchmark_test.go index 251ff7793c..f7d5de1d90 100644 --- a/api/file_benchmark_test.go +++ b/api/file_benchmark_test.go @@ -38,7 +38,7 @@ func BenchmarkGetFile(b *testing.B) { newProps["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(newProps) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ImageSettings.PublicLinkSalt)) // wait a bit for files to ready time.Sleep(5 * time.Second) diff --git a/api/file_test.go b/api/file_test.go index a0a2f32551..072a3fab15 100644 --- a/api/file_test.go +++ b/api/file_test.go @@ -222,7 +222,7 @@ func TestGetFile(t *testing.T) { newProps["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(newProps) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ImageSettings.PublicLinkSalt)) Client.LoginByEmail(team2.Name, user2.Email, "pwd") diff --git a/api/server.go b/api/server.go index a2f5fe552b..3f23d8df63 100644 --- a/api/server.go +++ b/api/server.go @@ -38,7 +38,7 @@ func NewServer() { func StartServer() { l4g.Info("Starting Server...") - l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.Port) + l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.ListenAddress) var handler http.Handler = Srv.Router @@ -71,7 +71,7 @@ func StartServer() { } go func() { - err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, handler) + err := Srv.Server.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handler) if err != nil { l4g.Critical("Error starting server, err:%v", err) time.Sleep(time.Second) diff --git a/api/team.go b/api/team.go index 8802208f73..c9d2412d37 100644 --- a/api/team.go +++ b/api/team.go @@ -38,7 +38,7 @@ func InitTeam(r *mux.Router) { } func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.EmailSettings.AllowSignUpWithEmail { + if !utils.Cfg.EmailSettings.EnableSignUpWithEmail { c.Err = model.NewAppError("signupTeam", "Team sign-up with email is disabled.", "") c.Err.StatusCode = http.StatusNotImplemented return @@ -66,7 +66,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash)) @@ -85,7 +85,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) { service := params["service"] sso := utils.Cfg.GetSSOService(service) - if sso != nil && !sso.Allow { + if sso != nil && !sso.Enable { c.SetInvalidParam("createTeamFromSSO", "service") return } @@ -142,7 +142,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) { } func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.EmailSettings.AllowSignUpWithEmail { + if !utils.Cfg.EmailSettings.EnableSignUpWithEmail { c.Err = model.NewAppError("createTeamFromSignup", "Team sign-up with email is disabled.", "") c.Err.StatusCode = http.StatusNotImplemented return @@ -183,7 +183,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { teamSignup.User.TeamId = "" teamSignup.User.Password = password - if !model.ComparePassword(teamSignup.Hash, fmt.Sprintf("%v:%v", teamSignup.Data, utils.Cfg.ServiceSettings.InviteSalt)) { + if !model.ComparePassword(teamSignup.Hash, fmt.Sprintf("%v:%v", teamSignup.Data, utils.Cfg.EmailSettings.InviteSalt)) { c.Err = model.NewAppError("createTeamFromSignup", "The signup link does not appear to be valid", "") return } @@ -243,7 +243,7 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { } func CreateTeam(c *Context, team *model.Team) *model.Team { - if !utils.Cfg.EmailSettings.AllowSignUpWithEmail { + if !utils.Cfg.EmailSettings.EnableSignUpWithEmail { c.Err = model.NewAppError("createTeam", "Team sign-up with email is disabled.", "") c.Err.StatusCode = http.StatusNotImplemented return nil @@ -258,11 +258,6 @@ func CreateTeam(c *Context, team *model.Team) *model.Team { return nil } - if utils.Cfg.ServiceSettings.Mode != utils.MODE_DEV { - c.Err = model.NewAppError("CreateTeam", "The mode does not allow network creation without a valid invite", "") - return nil - } - if result := <-Srv.Store.Team().Save(team); result.Err != nil { c.Err = result.Err return nil @@ -488,10 +483,10 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str props["name"] = team.Name props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash)) - if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { + if !utils.Cfg.EmailSettings.SendEmailNotifications { l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"]) } diff --git a/api/team_test.go b/api/team_test.go index 48c73c638f..cd39dacfe5 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -30,7 +30,7 @@ func TestCreateFromSignupTeam(t *testing.T) { props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"} diff --git a/api/user.go b/api/user.go index ba5323d776..d61afb0277 100644 --- a/api/user.go +++ b/api/user.go @@ -58,7 +58,7 @@ func InitUser(r *mux.Router) { } func createUser(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.EmailSettings.AllowSignUpWithEmail { + if !utils.Cfg.EmailSettings.EnableSignUpWithEmail { c.Err = model.NewAppError("signupTeam", "User sign-up with email is disabled.", "") c.Err.StatusCode = http.StatusNotImplemented return @@ -90,7 +90,7 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { data := r.URL.Query().Get("d") props := model.MapFromJson(strings.NewReader(data)) - if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) { + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) { c.Err = model.NewAppError("createUser", "The signup link does not appear to be valid", "") return } @@ -287,7 +287,7 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam func checkUserPassword(c *Context, user *model.User, password string) bool { - if user.FailedAttempts >= utils.Cfg.ServiceSettings.AllowedLoginAttempts { + if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts { c.LogAuditWithUserId(user.Id, "fail") c.Err = model.NewAppError("checkUserPassword", "Your account is locked because of too many failed password attempts. Please reset your password.", "user_id="+user.Id) c.Err.StatusCode = http.StatusForbidden @@ -1129,7 +1129,7 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) { newProps["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(newProps) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.ResetSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.PasswordResetSalt)) link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash)) @@ -1208,7 +1208,7 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.ServiceSettings.ResetSalt)) { + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) { c.Err = model.NewAppError("resetPassword", "The reset password link does not appear to be valid", "") return } @@ -1357,7 +1357,7 @@ func getStatuses(c *Context, w http.ResponseWriter, r *http.Request) { func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, teamName, service, redirectUri, loginHint string) { sso := utils.Cfg.GetSSOService(service) - if sso != nil && !sso.Allow { + if sso != nil && !sso.Enable { c.Err = model.NewAppError("GetAuthorizationCode", "Unsupported OAuth service provider", "service="+service) c.Err.StatusCode = http.StatusBadRequest return @@ -1385,7 +1385,7 @@ func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, te func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.Team, *model.AppError) { sso := utils.Cfg.GetSSOService(service) - if sso != nil && !sso.Allow { + if sso != nil && !sso.Enable { return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Unsupported OAuth service provider", "service="+service) } diff --git a/api/user_test.go b/api/user_test.go index 7451cb615a..34eefce59f 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -151,7 +151,7 @@ func TestLogin(t *testing.T) { props["display_name"] = rteam2.Data.(*model.Team).DisplayName props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash) @@ -814,7 +814,7 @@ func TestResetPassword(t *testing.T) { props["user_id"] = user.Id props["time"] = fmt.Sprintf("%v", model.GetMillis()) data["data"] = model.MapToJson(props) - data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.ServiceSettings.ResetSalt)) + data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) data["name"] = team.Name if _, err := Client.ResetPassword(data); err != nil { diff --git a/api/web_socket_test.go b/api/web_socket_test.go index 161274ff79..d086308bf5 100644 --- a/api/web_socket_test.go +++ b/api/web_socket_test.go @@ -16,7 +16,7 @@ import ( func TestSocket(t *testing.T) { Setup() - url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket" + url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/websocket" team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) diff --git a/config/config.json b/config/config.json index 12926bee57..4b5a16300a 100644 --- a/config/config.json +++ b/config/config.json @@ -1,18 +1,11 @@ { "ServiceSettings": { - "Mode": "dev", - "AllowTesting": false, - "UseSSL": false, - "Port": "8065", - "Version": "developer", - "InviteSalt": "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6", - "PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4", - "ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t", - "AnalyticsUrl": "", - "AllowedLoginAttempts": 10, - "EnableOAuthServiceProvider": false, + "ListenAddress": ":8065", + "MaximumLoginAttempts": 10, "SegmentDeveloperKey": "", - "GoogleDeveloperKey": "" + "GoogleDeveloperKey": "", + "EnableOAuthServiceProvider": false, + "EnableTesting": false }, "TeamSettings": { "SiteName": "Mattermost", @@ -32,9 +25,9 @@ "AtRestEncryptKey": "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV" }, "LogSettings": { - "ConsoleEnable": true, + "EnableConsole": true, "ConsoleLevel": "DEBUG", - "FileEnable": true, + "EnableFile": true, "FileLevel": "INFO", "FileFormat": "", "FileLocation": "" @@ -43,6 +36,7 @@ "DriverName": "local", "Directory": "./data/", "EnablePublicLink": true, + "PublicLinkSalt": "LhaAWC6lYEKHTkBKsvyXNIOfUIT37AX", "ThumbnailWidth": 120, "ThumbnailHeight": 100, "PreviewWidth": 1024, @@ -56,7 +50,7 @@ "AmazonS3Region": "" }, "EmailSettings": { - "AllowSignUpWithEmail": true, + "EnableSignUpWithEmail": true, "SendEmailNotifications": false, "RequireEmailVerification": false, "FeedbackName": "", @@ -66,6 +60,8 @@ "SMTPServer": "", "SMTPPort": "", "ConnectionSecurity": "", + "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo", + "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5e", "ApplePushServer": "", "ApplePushCertPublic": "", "ApplePushCertPrivate": "" @@ -82,7 +78,7 @@ "ShowFullName": true }, "GitLabSettings": { - "Allow": false, + "Enable": false, "Secret": "", "Id": "", "Scope": "", diff --git a/docker/dev/config_docker.json b/docker/dev/config_docker.json index aceeb95b44..8bd5f1b0a5 100644 --- a/docker/dev/config_docker.json +++ b/docker/dev/config_docker.json @@ -1,81 +1,73 @@ { + "ServiceSettings": { + "ListenAddress": ":80", + "MaximumLoginAttempts": 10, + "SegmentDeveloperKey": "", + "GoogleDeveloperKey": "", + "EnableOAuthServiceProvider": false, + "EnableTesting": false + }, + "TeamSettings": { + "SiteName": "Mattermost", + "MaxUsersPerTeam": 50, + "DefaultThemeColor": "#2389D7", + "EnableTeamCreation": true, + "EnableUserCreation": true, + "RestrictCreationToDomains": "" + }, + "SqlSettings": { + "DriverName": "mysql", + "DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8", + "DataSourceReplicas": [], + "MaxIdleConns": 10, + "MaxOpenConns": 10, + "Trace": false, + "AtRestEncryptKey": "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV" + }, "LogSettings": { - "ConsoleEnable": true, + "EnableConsole": false, "ConsoleLevel": "INFO", - "FileEnable": true, + "EnableFile": true, "FileLevel": "INFO", "FileFormat": "", "FileLocation": "" }, - "ServiceSettings": { - "SiteName": "Mattermost", - "Mode" : "dev", - "AllowTesting" : true, - "UseSSL": false, - "Port": "80", - "Version": "developer", - "Shards": { - }, - "InviteSalt": "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6", - "PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4", - "ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t", - "AnalyticsUrl": "", - "UseLocalStorage": true, - "StorageDirectory": "/mattermost/data/", - "AllowedLoginAttempts": 10, - "DisableEmailSignUp": false, - "EnableOAuthServiceProvider": false - }, - "SSOSettings": { - "gitlab": { - "Allow": false, - "Secret" : "", - "Id": "", - "Scope": "", - "AuthEndpoint": "", - "TokenEndpoint": "", - "UserApiEndpoint": "" - } - }, - "SqlSettings": { - "DriverName": "mysql", - "DataSource": "mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8", - "DataSourceReplicas": ["mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8"], - "MaxIdleConns": 10, - "MaxOpenConns": 10, - "Trace": false, - "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" - }, - "AWSSettings": { - "S3AccessKeyId": "", - "S3SecretAccessKey": "", - "S3Bucket": "", - "S3Region": "" - }, "ImageSettings": { + "DriverName": "local", + "Directory": "/mattermost/data/", + "EnablePublicLink": true, + "PublicLinkSalt": "LhaAWC6lYEKHTkBKsvyXNIOfUIT37AX", "ThumbnailWidth": 120, "ThumbnailHeight": 100, "PreviewWidth": 1024, "PreviewHeight": 0, "ProfileWidth": 128, "ProfileHeight": 128, - "InitialFont": "luximbi.ttf" + "InitialFont": "luximbi.ttf", + "AmazonS3AccessKeyId": "", + "AmazonS3SecretAccessKey": "", + "AmazonS3Bucket": "", + "AmazonS3Region": "" }, "EmailSettings": { - "ByPassEmail" : true, + "EnableSignUpWithEmail": true, + "SendEmailNotifications": false, + "RequireEmailVerification": false, + "FeedbackName": "", + "FeedbackEmail": "", "SMTPUsername": "", "SMTPPassword": "", "SMTPServer": "", - "UseTLS": false, - "UseStartTLS": false, - "FeedbackEmail": "", - "FeedbackName": "", + "SMTPPort": "", + "ConnectionSecurity": "", + "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo", + "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5e", "ApplePushServer": "", "ApplePushCertPublic": "", "ApplePushCertPrivate": "" }, "RateLimitSettings": { - "UseRateLimiter": true, + "EnableRateLimiter": true, "PerSec": 10, "MemoryStoreSize": 10000, "VaryByRemoteAddr": true, @@ -83,22 +75,15 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowPhoneNumber": true, - "ShowSkypeId": true, "ShowFullName": true }, - "TeamSettings": { - "MaxUsersPerTeam": 150, - "AllowPublicLink": true, - "AllowValetDefault": false, - "TermsLink": "/static/help/configure_links.html", - "PrivacyLink": "/static/help/configure_links.html", - "AboutLink": "/static/help/configure_links.html", - "HelpLink": "/static/help/configure_links.html", - "ReportProblemLink": "/static/help/configure_links.html", - "TourLink": "/static/help/configure_links.html", - "DefaultThemeColor": "#2389D7", - "DisableTeamCreation": false, - "RestrictCreationToDomains": "" + "GitLabSettings": { + "Enable": false, + "Secret": "", + "Id": "", + "Scope": "", + "AuthEndpoint": "", + "TokenEndpoint": "", + "UserApiEndpoint": "" } -} +} \ No newline at end of file diff --git a/docker/local/config_docker.json b/docker/local/config_docker.json index bc42951b8a..8bd5f1b0a5 100644 --- a/docker/local/config_docker.json +++ b/docker/local/config_docker.json @@ -1,81 +1,73 @@ { + "ServiceSettings": { + "ListenAddress": ":80", + "MaximumLoginAttempts": 10, + "SegmentDeveloperKey": "", + "GoogleDeveloperKey": "", + "EnableOAuthServiceProvider": false, + "EnableTesting": false + }, + "TeamSettings": { + "SiteName": "Mattermost", + "MaxUsersPerTeam": 50, + "DefaultThemeColor": "#2389D7", + "EnableTeamCreation": true, + "EnableUserCreation": true, + "RestrictCreationToDomains": "" + }, + "SqlSettings": { + "DriverName": "mysql", + "DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8", + "DataSourceReplicas": [], + "MaxIdleConns": 10, + "MaxOpenConns": 10, + "Trace": false, + "AtRestEncryptKey": "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV" + }, "LogSettings": { - "ConsoleEnable": true, + "EnableConsole": false, "ConsoleLevel": "INFO", - "FileEnable": true, + "EnableFile": true, "FileLevel": "INFO", "FileFormat": "", "FileLocation": "" }, - "ServiceSettings": { - "SiteName": "Mattermost", - "Mode" : "dev", - "AllowTesting" : true, - "UseSSL": false, - "Port": "80", - "Version": "developer", - "Shards": { - }, - "InviteSalt": "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6", - "PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4", - "ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t", - "AnalyticsUrl": "", - "UseLocalStorage": true, - "StorageDirectory": "/mattermost/data/", - "AllowedLoginAttempts": 10, - "DisableEmailSignUp": false, - "EnableOAuthServiceProvider": false - }, - "SSOSettings": { - "gitlab": { - "Allow": false, - "Secret" : "", - "Id": "", - "Scope": "", - "AuthEndpoint": "", - "TokenEndpoint": "", - "UserApiEndpoint": "" - } - }, - "SqlSettings": { - "DriverName": "mysql", - "DataSource": "mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8", - "DataSourceReplicas": ["mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8"], - "MaxIdleConns": 10, - "MaxOpenConns": 10, - "Trace": false, - "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" - }, - "AWSSettings": { - "S3AccessKeyId": "", - "S3SecretAccessKey": "", - "S3Bucket": "", - "S3Region": "" - }, "ImageSettings": { + "DriverName": "local", + "Directory": "/mattermost/data/", + "EnablePublicLink": true, + "PublicLinkSalt": "LhaAWC6lYEKHTkBKsvyXNIOfUIT37AX", "ThumbnailWidth": 120, "ThumbnailHeight": 100, "PreviewWidth": 1024, "PreviewHeight": 0, "ProfileWidth": 128, "ProfileHeight": 128, - "InitialFont": "luximbi.ttf" + "InitialFont": "luximbi.ttf", + "AmazonS3AccessKeyId": "", + "AmazonS3SecretAccessKey": "", + "AmazonS3Bucket": "", + "AmazonS3Region": "" }, "EmailSettings": { - "ByPassEmail" : true, + "EnableSignUpWithEmail": true, + "SendEmailNotifications": false, + "RequireEmailVerification": false, + "FeedbackName": "", + "FeedbackEmail": "", "SMTPUsername": "", "SMTPPassword": "", "SMTPServer": "", - "UseTLS": false, - "UseStartTLS": false, - "FeedbackEmail": "", - "FeedbackName": "", + "SMTPPort": "", + "ConnectionSecurity": "", + "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo", + "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5e", "ApplePushServer": "", "ApplePushCertPublic": "", "ApplePushCertPrivate": "" }, "RateLimitSettings": { - "UseRateLimiter": true, + "EnableRateLimiter": true, "PerSec": 10, "MemoryStoreSize": 10000, "VaryByRemoteAddr": true, @@ -83,22 +75,15 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowPhoneNumber": true, - "ShowSkypeId": true, "ShowFullName": true }, - "TeamSettings": { - "MaxUsersPerTeam": 150, - "AllowPublicLink": true, - "AllowValetDefault": false, - "TermsLink": "/static/help/configure_links.html", - "PrivacyLink": "/static/help/configure_links.html", - "AboutLink": "/static/help/configure_links.html", - "HelpLink": "/static/help/configure_links.html", - "ReportProblemLink": "/static/help/configure_links.html", - "TourLink": "/static/help/configure_links.html", - "DefaultThemeColor": "#2389D7", - "DisableTeamCreation": false, - "RestrictCreationToDomains": "" + "GitLabSettings": { + "Enable": false, + "Secret": "", + "Id": "", + "Scope": "", + "AuthEndpoint": "", + "TokenEndpoint": "", + "UserApiEndpoint": "" } -} +} \ No newline at end of file diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index 86b173c6a2..a517b0c0e1 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -53,7 +53,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { } // Create a client for tests to use - client := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port) + client := model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) // Check for username parameter and create a user if present username, ok1 := params["username"] diff --git a/mattermost.go b/mattermost.go index f54bcf15fb..4608bfff3e 100644 --- a/mattermost.go +++ b/mattermost.go @@ -57,7 +57,7 @@ func main() { api.StartServer() // If we allow testing then listen for manual testing URL hits - if utils.Cfg.ServiceSettings.AllowTesting { + if utils.Cfg.ServiceSettings.EnableTesting { manualtesting.InitManualTesting() } diff --git a/model/client.go b/model/client.go index 823e859cf1..d13ffa6cb4 100644 --- a/model/client.go +++ b/model/client.go @@ -21,6 +21,7 @@ const ( HEADER_ETAG_SERVER = "ETag" HEADER_ETAG_CLIENT = "If-None-Match" HEADER_FORWARDED = "X-Forwarded-For" + HEADER_REAL_IP = "X-Real-IP" HEADER_FORWARDED_PROTO = "X-Forwarded-Proto" HEADER_TOKEN = "token" HEADER_BEARER = "BEARER" diff --git a/model/config.go b/model/config.go index 876c36e985..7791c40212 100644 --- a/model/config.go +++ b/model/config.go @@ -20,23 +20,16 @@ const ( ) type ServiceSettings struct { - Mode string - AllowTesting bool - UseSSL bool - Port string - Version string - InviteSalt string - PublicLinkSalt string - ResetSalt string - AnalyticsUrl string - AllowedLoginAttempts int - EnableOAuthServiceProvider bool + ListenAddress string + MaximumLoginAttempts int SegmentDeveloperKey string GoogleDeveloperKey string + EnableOAuthServiceProvider bool + EnableTesting bool } type SSOSettings struct { - Allow bool + Enable bool Secret string Id string Scope string @@ -56,9 +49,9 @@ type SqlSettings struct { } type LogSettings struct { - ConsoleEnable bool + EnableConsole bool ConsoleLevel string - FileEnable bool + EnableFile bool FileLevel string FileFormat string FileLocation string @@ -68,6 +61,7 @@ type ImageSettings struct { DriverName string Directory string EnablePublicLink bool + PublicLinkSalt string ThumbnailWidth uint ThumbnailHeight uint PreviewWidth uint @@ -82,7 +76,7 @@ type ImageSettings struct { } type EmailSettings struct { - AllowSignUpWithEmail bool + EnableSignUpWithEmail bool SendEmailNotifications bool RequireEmailVerification bool FeedbackName string @@ -92,6 +86,8 @@ type EmailSettings struct { SMTPServer string SMTPPort string ConnectionSecurity string + InviteSalt string + PasswordResetSalt string // For Future Use ApplePushServer string diff --git a/utils/config.go b/utils/config.go index 45f62dc197..c2466800e8 100644 --- a/utils/config.go +++ b/utils/config.go @@ -58,9 +58,9 @@ func FindDir(dir string) string { func ConfigureCmdLineLog() { ls := model.LogSettings{} - ls.ConsoleEnable = true + ls.EnableConsole = true ls.ConsoleLevel = "ERROR" - ls.FileEnable = false + ls.EnableFile = false configureLog(&ls) } @@ -68,7 +68,7 @@ func configureLog(s *model.LogSettings) { l4g.Close() - if s.ConsoleEnable { + if s.EnableConsole { level := l4g.DEBUG if s.ConsoleLevel == "INFO" { level = l4g.INFO @@ -79,7 +79,7 @@ func configureLog(s *model.LogSettings) { l4g.AddFilter("stdout", level, l4g.NewConsoleLogWriter()) } - if s.FileEnable { + if s.EnableFile { var fileFormat = s.FileFormat @@ -174,16 +174,15 @@ func getClientProperties(c *model.Config) map[string]string { props["BuildHash"] = model.BuildHash props["SiteName"] = c.TeamSettings.SiteName - props["AnalyticsUrl"] = c.ServiceSettings.AnalyticsUrl props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider) props["SegmentDeveloperKey"] = c.ServiceSettings.SegmentDeveloperKey props["GoogleDeveloperKey"] = c.ServiceSettings.GoogleDeveloperKey props["SendEmailNotifications"] = strconv.FormatBool(c.EmailSettings.SendEmailNotifications) - props["AllowSignUpWithEmail"] = strconv.FormatBool(c.EmailSettings.AllowSignUpWithEmail) + props["EnableSignUpWithEmail"] = strconv.FormatBool(c.EmailSettings.EnableSignUpWithEmail) props["FeedbackEmail"] = c.EmailSettings.FeedbackEmail - props["AllowSignUpWithGitLab"] = strconv.FormatBool(c.GitLabSettings.Allow) + props["EnableSignUpWithGitLab"] = strconv.FormatBool(c.GitLabSettings.Enable) props["ShowEmailAddress"] = strconv.FormatBool(c.PrivacySettings.ShowEmailAddress) diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx index 72b5d5c9da..ce7d61ca93 100644 --- a/web/react/components/admin_console/admin_controller.jsx +++ b/web/react/components/admin_console/admin_controller.jsx @@ -15,7 +15,7 @@ var RateSettingsTab = require('./rate_settings.jsx'); var GitLabSettingsTab = require('./gitlab_settings.jsx'); var SqlSettingsTab = require('./sql_settings.jsx'); var TeamSettingsTab = require('./team_settings.jsx'); - +var ServiceSettingsTab = require('./service_settings.jsx'); export default class AdminController extends React.Component { constructor(props) { @@ -26,7 +26,7 @@ export default class AdminController extends React.Component { this.state = { config: null, - selected: 'team_settings' + selected: 'service_settings' }; } @@ -72,6 +72,8 @@ export default class AdminController extends React.Component { tab = ; } else if (this.state.selected === 'team_settings') { tab = ; + } else if (this.state.selected === 'service_settings') { + tab = ; } } diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx index 2b7159e1da..0983c12765 100644 --- a/web/react/components/admin_console/admin_sidebar.jsx +++ b/web/react/components/admin_console/admin_sidebar.jsx @@ -38,6 +38,15 @@ export default class AdminSidebar extends React.Component {