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
Этот коммит содержится в:
@@ -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())
|
||||
|
||||
Ссылка в новой задаче
Block a user