MM-10516: Added support for PostActions in ephemeral posts (#10258)

* Added support for PostActions in ephemeral posts

The general approach is that we take all the metadata that DoPostAction
needs to process client DoPostActionRequests, and store it in a
serialized, encrypted Cookie field, in the PostAction struct.

The client then must send it back, and it is then used to process
PostActions as a fallback top the metadata in the database.

This PR adds a new config setting, `ServiceSettings.ActionCookieSecret`.
In a cluster environment it must be the same for all instances.

- Added type PostActionCookie, and a Cookie string to PostAction.
- Added App.AddActionCookiesToPost.
- Use App.AddActionCookiesToPost in api4.createEphemeralPost,
  App.SendEphemeralPost, App.UpdateEphemeralPost.
- Added App.DoPostActionWithCookie to process incoming requests with
  cookies. For backward compatibility, it prefers the metadata in the
  database; falls back to cookie.
- Added plugin.API.UpdateEphemeralPost and plugin.API.DeleteEphemeralPost.
- Added App.encryptActionCookie/App.decryptActionCookie.

* Style

* Fixed an unfortunate typo, tested with matterpoll

* minor PR feedback

* Fixed uninitialized Context

* Fixed another test failure

* Fixed permission check

* Added api test for DoPostActionWithCookie

* Replaced config.ActionCookieSecret with Server.PostActionCookieSecret

Modeled after AsymetricSigningKey

* style

* Set DeleteAt in DeleteEphemeralPost

* PR feedback

* Removed deadwood comment

* Added EXPERIMENTAL comment to the 2 APIs in question
Этот коммит содержится в:
Lev
2019-03-01 10:15:31 -08:00
коммит произвёл GitHub
родитель dcf611b735
Коммит 3ad901b50b
17 изменённых файлов: 611 добавлений и 85 удалений

Просмотреть файл

@@ -100,6 +100,64 @@ func (a *App) RemoveConfigListener(id string) {
a.Srv.RemoveConfigListener(id)
}
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
// and future calls to PostAcrionCookieSecret will always return a valid key, same on all
// servers in the cluster
func (a *App) ensurePostActionCookieSecret() error {
if a.Srv.postActionCookieSecret != nil {
return nil
}
var secret *model.SystemPostActionCookieSecret
result := <-a.Srv.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
if result.Err == nil {
if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &secret); err != nil {
return err
}
}
// If we don't already have a key, try to generate one.
if secret == nil {
newSecret := &model.SystemPostActionCookieSecret{
Secret: make([]byte, 32),
}
_, err := rand.Reader.Read(newSecret.Secret)
if err != nil {
return err
}
system := &model.System{
Name: model.SYSTEM_POST_ACTION_COOKIE_SECRET,
}
v, err := json.Marshal(newSecret)
if err != nil {
return err
}
system.Value = string(v)
if result = <-a.Srv.Store.System().Save(system); result.Err == nil {
// If we were able to save the key, use it, otherwise ignore the error.
secret = newSecret
}
}
// If we weren't able to save a new key above, another server must have beat us to it. Get the
// key from the database, and if that fails, error out.
if secret == nil {
result := <-a.Srv.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
if result.Err != nil {
return result.Err
}
if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &secret); err != nil {
return err
}
}
a.Srv.postActionCookieSecret = secret.Secret
return nil
}
// EnsureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to
// AsymmetricSigningKey will always return a valid signing key.
func (a *App) ensureAsymmetricSigningKey() error {
@@ -209,6 +267,14 @@ func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
return a.Srv.AsymmetricSigningKey()
}
func (s *Server) PostActionCookieSecret() []byte {
return s.postActionCookieSecret
}
func (a *App) PostActionCookieSecret() []byte {
return a.Srv.PostActionCookieSecret()
}
func (a *App) regenerateClientConfig() {
clientConfig := config.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License())
limitedClientConfig := config.GenerateLimitedClientConfig(a.Config(), a.DiagnosticId(), a.License())