package userapi import "context" // AccountObserver is told when an account is deactivated, after the // account itself has been dealt with. The roomserver's side of a // deactivation — leaving every room — is done through it, because nothing // in this package knows about rooms. // // Like DeviceObserver it runs on the request path and must not fail the // deactivation: the account is already deactivated when it is called. type AccountObserver interface { AccountDeactivated(ctx context.Context, localpart string) } // SetAccountObserver connects the observer. It is a setter for the same // reason SetDeviceObserver is: the roomserver is built after the userapi. func (a *API) SetAccountObserver(o AccountObserver) { a.accounts = o } // DeactivateAccount deactivates an account: what both the user's own // POST /account/deactivate and the admin API's deactivation do. // // The order is deliberate. The flag goes first, so that every token the // account still holds stops working at once, before anything slower runs; // the profile is erased next, so that the leave events the observer sends // into the user's rooms already carry no name or avatar; the devices go // after that, taking their keys and pushers with them through the device // observer; and the rooms are left last. // // The name stays taken. A deactivated account cannot be registered again, // because a new user with the old user's ID would inherit everything other // servers and clients remember about the old one. func (a *API) DeactivateAccount(ctx context.Context, localpart string, erase bool) error { if err := a.store.SetDeactivated(ctx, localpart, true); err != nil { return err } if erase { if err := a.store.SetDisplayName(ctx, localpart, ""); err != nil { return err } if err := a.store.SetAvatarURL(ctx, localpart, ""); err != nil { return err } } if err := a.DeleteAllDevices(ctx, localpart); err != nil { return err } if a.accounts != nil { a.accounts.AccountDeactivated(ctx, localpart) } a.log.Info("deactivated account", "user_id", a.UserID(localpart), "erase", erase) return nil } // ReactivateAccount clears the deactivated flag, for the admin API. It // gives back nothing a deactivation took — the rooms are left and the // devices gone — only the ability to log in again. func (a *API) ReactivateAccount(ctx context.Context, localpart string) error { if err := a.store.SetDeactivated(ctx, localpart, false); err != nil { return err } a.log.Info("reactivated account", "user_id", a.UserID(localpart)) return nil } // ChangePassword sets a new password and, when logoutDevices is set, logs // out every device of the user except keepDeviceID (empty keeps none). // // A user changing their own password keeps the session they changed it // from; an admin resetting one keeps none, since whoever is locked out of // the account may be the one holding the sessions. func (a *API) ChangePassword(ctx context.Context, localpart, password string, logoutDevices bool, keepDeviceID string) error { if err := a.SetPassword(ctx, localpart, password); err != nil { return err } if !logoutDevices { return nil } if keepDeviceID == "" { return a.DeleteAllDevices(ctx, localpart) } devices, err := a.store.Devices(ctx, localpart) if err != nil { return err } others := make([]string, 0, len(devices)) for _, d := range devices { if d.ID != keepDeviceID { others = append(others, d.ID) } } return a.DeleteDevices(ctx, localpart, others) }