diff options
-rw-r--r-- | api/api.go | 2 | ||||
-rw-r--r-- | api/context.go | 34 | ||||
-rw-r--r-- | api/server.go | 8 | ||||
-rw-r--r-- | config/config.json | 1 | ||||
-rw-r--r-- | i18n/en.json | 2 | ||||
-rw-r--r-- | i18n/es.json | 2 | ||||
-rw-r--r-- | i18n/pt.json | 4 | ||||
-rw-r--r-- | mattermost.go | 2 | ||||
-rw-r--r-- | model/config.go | 6 | ||||
-rw-r--r-- | utils/config.go | 2 | ||||
-rw-r--r-- | web/react/components/admin_console/service_settings.jsx | 35 | ||||
-rw-r--r-- | web/react/components/get_team_invite_link_modal.jsx | 4 | ||||
-rw-r--r-- | web/static/i18n/en.json | 5 | ||||
-rw-r--r-- | web/static/i18n/es.json | 4 | ||||
-rw-r--r-- | web/static/i18n/pt.json | 1 |
15 files changed, 99 insertions, 13 deletions
diff --git a/api/api.go b/api/api.go index d202172d0..4fecd3dd4 100644 --- a/api/api.go +++ b/api/api.go @@ -36,7 +36,7 @@ func (me *ServerTemplatePage) Render() string { T := utils.GetUserTranslations(me.Locale) me.Props["Footer"] = T("api.templates.email_footer") me.Html["EmailInfo"] = template.HTML(T("api.templates.email_info", - map[string]interface{}{"FeedbackEmail": me.ClientCfg["FeedbackEmail"], "SiteName": me.ClientCfg["SiteName"]})) + map[string]interface{}{"SupportEmail": me.ClientCfg["SupportEmail"], "SiteName": me.ClientCfg["SiteName"]})) if err := ServerTemplates.ExecuteTemplate(&text, me.TemplateName, me); err != nil { l4g.Error(utils.T("api.api.render.error"), me.TemplateName, err) diff --git a/api/context.go b/api/context.go index 9e05c5d87..edcdcbfef 100644 --- a/api/context.go +++ b/api/context.go @@ -21,6 +21,15 @@ import ( var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE) +var allowedMethods []string = []string{ + "POST", + "GET", + "OPTIONS", + "PUT", + "PATCH", + "DELETE", +} + type Context struct { Session model.Session RequestId string @@ -234,6 +243,31 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if len(*utils.Cfg.ServiceSettings.AllowCorsFrom) > 0 { + origin := r.Header.Get("Origin") + if *utils.Cfg.ServiceSettings.AllowCorsFrom == "*" || strings.Contains(*utils.Cfg.ServiceSettings.AllowCorsFrom, origin) { + w.Header().Set("Access-Control-Allow-Origin", origin) + + if r.Method == "OPTIONS" { + w.Header().Set( + "Access-Control-Allow-Methods", + strings.Join(allowedMethods, ", ")) + + w.Header().Set( + "Access-Control-Allow-Headers", + r.Header.Get("Access-Control-Request-Headers")) + } + } + } + + if r.Method == "OPTIONS" { + return + } + + cw.router.ServeHTTP(w, r) +} + func GetProtocol(r *http.Request) string { if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" { return "https" diff --git a/api/server.go b/api/server.go index 070ed7a70..b84066cbe 100644 --- a/api/server.go +++ b/api/server.go @@ -21,6 +21,10 @@ type Server struct { Router *mux.Router } +type CorsWrapper struct { + router *mux.Router +} + var Srv *Server func NewServer() { @@ -38,7 +42,7 @@ func StartServer() { l4g.Info(utils.T("api.server.start_server.starting.info")) l4g.Info(utils.T("api.server.start_server.listening.info"), utils.Cfg.ServiceSettings.ListenAddress) - var handler http.Handler = Srv.Router + var handler http.Handler = &CorsWrapper{Srv.Router} if utils.Cfg.RateLimitSettings.EnableRateLimiter { l4g.Info(utils.T("api.server.start_server.rate.info")) @@ -65,7 +69,7 @@ func StartServer() { throttled.DefaultDeniedHandler.ServeHTTP(w, r) }) - handler = th.Throttle(Srv.Router) + handler = th.Throttle(&CorsWrapper{Srv.Router}) } go func() { diff --git a/config/config.json b/config/config.json index 2795546f8..b211b16d3 100644 --- a/config/config.json +++ b/config/config.json @@ -15,6 +15,7 @@ "EnableDeveloper": false, "EnableSecurityFixAlert": true, "EnableInsecureOutgoingConnections": false, + "AllowCorsFrom": "", "SessionLengthWebInDays": 30, "SessionLengthMobileInDays": 30, "SessionLengthSSOInDays": 30, diff --git a/i18n/en.json b/i18n/en.json index 2b125feca..0f8b5de7c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1113,7 +1113,7 @@ }, { "id": "api.templates.email_info", - "translation": "Any questions at all, mail us any time: <a href='mailto:{{.FeedbackEmail}}' style='text-decoration: none; color:#2389D7;'>{{.FeedbackEmail}}</a>.<br>Best wishes,<br>The {{.SiteName}} Team<br>" + "translation": "Any questions at all, mail us any time: <a href='mailto:{{.SupportEmail}}' style='text-decoration: none; color:#2389D7;'>{{.SupportEmail}}</a>.<br>Best wishes,<br>The {{.SiteName}} Team<br>" }, { "id": "api.templates.error.link", diff --git a/i18n/es.json b/i18n/es.json index 1426195dd..6670b7f5b 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -1113,7 +1113,7 @@ }, { "id": "api.templates.email_info", - "translation": "Si tienes alguna pregunta, escribenos en cualquier momento a: <a href=\"mailto:{{.FeedbackEmail}}\" style=\"text-decoration: none; color:#2389D7;\">{{.FeedbackEmail}}</a>.<br>Los mejores deseos,<br>El Equipo {{.SiteName}}<br>" + "translation": "Si tienes alguna pregunta, escribenos en cualquier momento a: <a href=\"mailto:{{.SupportEmail}}\" style=\"text-decoration: none; color:#2389D7;\">{{.SupportEmail}}</a>.<br>Los mejores deseos,<br>El Equipo {{.SiteName}}<br>" }, { "id": "api.templates.error.link", diff --git a/i18n/pt.json b/i18n/pt.json index a70cccf4d..e01db6b22 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -1113,7 +1113,7 @@ }, { "id": "api.templates.email_info", - "translation": "Qualquer dúvida, envie-nos a qualquer momento: <a href='mailto:{{.FeedbackEmail}}' style='text-decoration: none; color:#2389D7;'>{{.FeedbackEmail}}</a>.<br>Nossos melhores cumprimentos,<br>Equipe {{.SiteName}}<br>" + "translation": "Qualquer dúvida, envie-nos a qualquer momento: <a href='mailto:{{.SupportEmail}}' style='text-decoration: none; color:#2389D7;'>{{.SupportEmail}}</a>.<br>Nossos melhores cumprimentos,<br>Equipe {{.SiteName}}<br>" }, { "id": "api.templates.error.link", @@ -3587,4 +3587,4 @@ "id": "web.watcher_fail.error", "translation": "Falha ao adicionar diretório observador %v" } -]
\ No newline at end of file +] diff --git a/mattermost.go b/mattermost.go index 7446fe791..b305aac3c 100644 --- a/mattermost.go +++ b/mattermost.go @@ -282,7 +282,7 @@ func cmdCreateTeam() { team.DisplayName = flagTeamName team.Name = flagTeamName team.Email = flagEmail - team.Type = model.TEAM_INVITE + team.Type = model.TEAM_OPEN api.CreateTeam(c, team) if c.Err != nil { diff --git a/model/config.go b/model/config.go index aa3dd3586..82c51224e 100644 --- a/model/config.go +++ b/model/config.go @@ -39,6 +39,7 @@ type ServiceSettings struct { EnableDeveloper *bool EnableSecurityFixAlert *bool EnableInsecureOutgoingConnections *bool + AllowCorsFrom *string SessionLengthWebInDays *int SessionLengthMobileInDays *int SessionLengthSSOInDays *int @@ -377,6 +378,11 @@ func (o *Config) SetDefaults() { o.ServiceSettings.WebsocketSecurePort = new(int) *o.ServiceSettings.WebsocketSecurePort = 443 } + + if o.ServiceSettings.AllowCorsFrom == nil { + o.ServiceSettings.AllowCorsFrom = new(string) + *o.ServiceSettings.AllowCorsFrom = "" + } } func (o *Config) IsValid() *AppError { diff --git a/utils/config.go b/utils/config.go index 3e4ba5c5b..63906c345 100644 --- a/utils/config.go +++ b/utils/config.go @@ -236,5 +236,7 @@ func getClientConfig(c *model.Config) map[string]string { props["WebsocketPort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketPort) props["WebsocketSecurePort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketSecurePort) + props["AllowCorsFrom"] = *c.ServiceSettings.AllowCorsFrom + return props } diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx index 047c7eb8d..9ed81b6a3 100644 --- a/web/react/components/admin_console/service_settings.jsx +++ b/web/react/components/admin_console/service_settings.jsx @@ -31,6 +31,10 @@ var holders = defineMessages({ id: 'admin.service.sessionDaysEx', defaultMessage: 'Ex "30"' }, + corsExample: { + id: 'admin.service.corsEx', + defaultMessage: 'http://example.com' + }, saving: { id: 'admin.service.saving', defaultMessage: 'Saving Config...' @@ -131,6 +135,8 @@ class ServiceSettings extends React.Component { config.ServiceSettings.SessionCacheInMinutes = SessionCacheInMinutes; ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value = SessionCacheInMinutes; + config.ServiceSettings.AllowCorsFrom = ReactDOM.findDOMNode(this.refs.AllowCorsFrom).value.trim(); + Client.saveConfig( config, () => { @@ -766,6 +772,35 @@ class ServiceSettings extends React.Component { <div className='form-group'> <label className='control-label col-sm-4' + htmlFor='AllowCorsFrom' + > + <FormattedMessage + id='admin.service.corsTitle' + defaultMessage='Allow Cross-origin Requests from:' + /> + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='AllowCorsFrom' + ref='AllowCorsFrom' + placeholder={formatMessage(holders.corsExample)} + defaultValue={this.props.config.ServiceSettings.AllowCorsFrom} + onChange={this.handleChange} + /> + <p className='help-text'> + <FormattedMessage + id='admin.service.corsDescription' + defaultMessage='Enable HTTP Cross origin request from a specific domain. Use "*" if you want to allow CORS from any domain or leave it blank to disable it.' + /> + </p> + </div> + </div> + + <div className='form-group'> + <label + className='control-label col-sm-4' htmlFor='SessionLengthWebInDays' > <FormattedMessage diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx index 299729250..ba6164dbf 100644 --- a/web/react/components/get_team_invite_link_modal.jsx +++ b/web/react/components/get_team_invite_link_modal.jsx @@ -15,7 +15,7 @@ const holders = defineMessages({ }, help: { id: 'get_team_invite_link_modal.help', - defaultMessage: 'Send teammates the link below for them to sign-up to this team site.' + defaultMessage: 'Send teammates the link below for them to sign-up to this team site. The Team Invite Link can be shared with multiple teammates as it does not change unless it\'s regenerated in Team Settings by a Team Admin.' }, helpDisabled: { id: 'get_team_invite_link_modal.helpDisabled', @@ -73,4 +73,4 @@ GetTeamInviteLinkModal.propTypes = { intl: intlShape.isRequired }; -export default injectIntl(GetTeamInviteLinkModal);
\ No newline at end of file +export default injectIntl(GetTeamInviteLinkModal); diff --git a/web/static/i18n/en.json b/web/static/i18n/en.json index 14ef0fb46..7d427935c 100644 --- a/web/static/i18n/en.json +++ b/web/static/i18n/en.json @@ -277,6 +277,9 @@ "admin.service.attemptTitle": "Maximum Login Attempts:", "admin.service.cmdsDesc": "When true, user created slash commands will be allowed.", "admin.service.cmdsTitle": "Enable Slash Commands: ", + "admin.service.corsEx": "http://example.com https://example.com", + "admin.service.corsDescription": "Enable HTTP Cross origin request from specific domains (separate by a spacebar). Use \"*\" if you want to allow CORS from any domain or leave it blank to disable it.", + "admin.service.corsTitle": "Allow Cross-origin Requests from:", "admin.service.developerDesc": "(Developer Option) When true, extra information around errors will be displayed in the UI.", "admin.service.developerTitle": "Enable Developer Mode: ", "admin.service.false": "false", @@ -699,7 +702,7 @@ "get_link.copy": "Copy Link", "get_post_link_modal.help": "The link below allows authorized users to see your post.", "get_post_link_modal.title": "Copy Permalink", - "get_team_invite_link_modal.help": "Send teammates the link below for them to sign-up to this team site.", + "get_team_invite_link_modal.help": "Send teammates the link below for them to sign-up to this team site. The Team Invite Link can be shared with multiple teammates as it does not change unless it's regenerated in Team Settings by a Team Admin.", "get_team_invite_link_modal.helpDisabled": "User creation has been disabled for your team. Please ask your team administrator for details.", "get_team_invite_link_modal.title": "Team Invite Link", "intro_messages.DM": "This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.", diff --git a/web/static/i18n/es.json b/web/static/i18n/es.json index 09abcf530..e3f23e77e 100644 --- a/web/static/i18n/es.json +++ b/web/static/i18n/es.json @@ -277,6 +277,9 @@ "admin.service.attemptTitle": "Máximo de intentos de conexión:", "admin.service.cmdsDesc": "Cuando es verdadero, se permite la creación de comandos de barra por usuarios.", "admin.service.cmdsTitle": "Habilitar Comandos de Barra: ", + "admin.service.corsEx": "http://ejemplo.com https://ejemplo.com", + "admin.service.corsDescription": "Habilita las solicitudes HTTP de origen cruzado para dominios en específico (separados por un espacio). Utiliza \"*\" si quieres habilitar CORS desde cualquier dominio o deja el campo en blanco para deshabilitarlo.", + "admin.service.corsTitle": "Permitir Solicitudes de Origen Cruzado desde:", "admin.service.developerDesc": "(Opción de Desarrollador) Cuando está asignado en verdadero, información extra sobre errores se muestra en el UI.", "admin.service.developerTitle": "Habilitar modo de Desarrollador: ", "admin.service.false": "falso", @@ -699,7 +702,6 @@ "get_link.copy": "Copiar Enlace", "get_post_link_modal.help": "En enlace de abajo permite a los usuarios autorizados a ver tu mensaje.", "get_post_link_modal.title": "Copiar enlace Permanente", - "get_team_invite_link_modal.help": "Enviar a los compañeros de equipo el enlace que se muestra a continuación para permitirles registrarse a este equipo.", "get_team_invite_link_modal.helpDisabled": "La creación de usuario ha sido deshabilitada para tu equipo. Por favor solicita más detalles a tu administrador de equipo.", "get_team_invite_link_modal.title": "Enlace de Invitación al Equipo", "intro_messages.DM": "Este es el inicio de tu historial de mensajes directos con {teammate}.<br />Los mensajes directos y archivos que se comparten aquí no son mostrados a personas fuera de esta área.", diff --git a/web/static/i18n/pt.json b/web/static/i18n/pt.json index ef5b9a2ab..f79dae461 100644 --- a/web/static/i18n/pt.json +++ b/web/static/i18n/pt.json @@ -699,7 +699,6 @@ "get_link.copy": "Copiar Link", "get_post_link_modal.help": "O link abaixo permite que usuários autorizados possam ver seus posts.", "get_post_link_modal.title": "Copiar Permalink", - "get_team_invite_link_modal.help": "Enviar a equipe de trabalho o link abaixo para eles se inscreverem nesta equipe.", "get_team_invite_link_modal.helpDisabled": "Criação de usuários está desabilitada para sua equipe. Por favor peça ao administrador de equipe por detalhes.", "get_team_invite_link_modal.title": "Link para Convite de Equipe", "intro_messages.DM": "Este é o início do seu histórico de mensagens diretas com {teammate}.<br />Mensagens diretas e arquivos compartilhados aqui não são mostrados para pessoas de fora desta área.", |