From 4b73df5335295bc5e50d864c7be2a561c6b912bc Mon Sep 17 00:00:00 2001 From: santhoshivan23 Date: Thu, 8 Jun 2023 00:39:58 +0530 Subject: [PATCH] Implement ListTokens --- clientapi/api/api.go | 8 ++ clientapi/routing/admin.go | 49 +++++++++++- clientapi/routing/routing.go | 8 +- userapi/api/api.go | 4 +- userapi/internal/user_api.go | 19 +++-- userapi/storage/interface.go | 4 +- .../postgres/registration_tokens_table.go | 74 +++++++++++++++++-- userapi/storage/shared/storage.go | 9 ++- userapi/storage/tables/interface.go | 4 +- 9 files changed, 157 insertions(+), 22 deletions(-) diff --git a/clientapi/api/api.go b/clientapi/api/api.go index 23974c865..28ff593fc 100644 --- a/clientapi/api/api.go +++ b/clientapi/api/api.go @@ -21,3 +21,11 @@ type ExtraPublicRoomsProvider interface { // Rooms returns the extra rooms. This is called on-demand by clients, so cache appropriately. Rooms() []fclient.PublicRoom } + +type RegistrationToken struct { + Token *string `json:"token"` + UsesAllowed *int32 `json:"uses_allowed"` + Pending *int32 `json:"pending"` + Completed *int32 `json:"completed"` + ExpiryTime *int64 `json:"expiry_time"` +} diff --git a/clientapi/routing/admin.go b/clientapi/routing/admin.go index f2a391c9c..d0608f7aa 100644 --- a/clientapi/routing/admin.go +++ b/clientapi/routing/admin.go @@ -8,6 +8,7 @@ import ( "math/rand" "net/http" "regexp" + "strconv" "strings" "time" @@ -20,6 +21,7 @@ import ( "github.com/nats-io/nats.go" "github.com/sirupsen/logrus" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/internal/httputil" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" @@ -101,13 +103,20 @@ func AdminCreateNewRegistrationToken(req *http.Request, cfg *config.ClientAPI, u string(spec.ErrorInvalidParam), "expiry_time must not be in the past") } - pending := 0 - completed := 0 + pending := int32(0) + completed := int32(0) // If usesAllowed or expiryTime is 0, it means they are not present in the request. NULL (indicating unlimited uses / no expiration will be persisted in DB) - created, err := userAPI.PerformAdminCreateRegistrationToken(req.Context(), token, usesAllowed, expiryTime) + registrationToken := &clientapi.RegistrationToken{ + Token: &token, + UsesAllowed: &usesAllowed, + Pending: &pending, + Completed: &completed, + ExpiryTime: &expiryTime, + } + created, err := userAPI.PerformAdminCreateRegistrationToken(req.Context(), registrationToken) if err != nil { return util.MatrixErrorResponse( - http.StatusInternalServerError, + http.StatusBadRequest, string(spec.ErrorUnknown), err.Error(), ) @@ -148,6 +157,38 @@ func getReturnValueForUsesAllowed(usesAllowed int32) interface{} { return usesAllowed } +func AdminListRegistrationTokens(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.MatrixErrorResponse( + http.StatusInternalServerError, + string(spec.ErrorInvalidParam), + "unable to parse query params", + ) + } + returnAll := true + validQuery, ok := vars["valid"] + if ok { + returnAll = false + } + valid, err := strconv.ParseBool(validQuery) + tokens, err := userAPI.PerformAdminListRegistrationTokens(req.Context(), returnAll, valid) + if err != nil { + return util.MatrixErrorResponse( + http.StatusInternalServerError, + string(spec.ErrorUnknown), + "error fetching registration tokens", + ) + } + + return util.JSONResponse{ + Code: 200, + JSON: map[string]interface{}{ + "registration_tokens": tokens, + }, + } +} + func getReturnValueExpiryTime(expiryTime int64) interface{} { if expiryTime == 0 { return nil diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index bbca60227..2d96e05cc 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -168,11 +168,17 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) + dendriteAdminRouter.Handle("/admin/registrationTokens", + httputil.MakeAdminAPI("admin_registration_tokens", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + return AdminListRegistrationTokens(req, cfg, userAPI) + }), + ).Methods(http.MethodGet, http.MethodOptions) + dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}", httputil.MakeAdminAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { return AdminEvacuateRoom(req, rsAPI) }), - ).Methods(http.MethodPost, http.MethodOptions) + ).Methods(http.MethodGet, http.MethodOptions) dendriteAdminRouter.Handle("/admin/evacuateUser/{userID}", httputil.MakeAdminAPI("admin_evacuate_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { diff --git a/userapi/api/api.go b/userapi/api/api.go index 1dfae8ed1..9f014d3f0 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -27,6 +27,7 @@ import ( "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/pushrules" ) @@ -94,7 +95,8 @@ type ClientUserAPI interface { QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error QueryPushRules(ctx context.Context, userID string) (*pushrules.AccountRuleSets, error) QueryAccountAvailability(ctx context.Context, req *QueryAccountAvailabilityRequest, res *QueryAccountAvailabilityResponse) error - PerformAdminCreateRegistrationToken(ctx context.Context, token string, usesAllowed int32, expiryTime int64) (bool, error) + PerformAdminCreateRegistrationToken(ctx context.Context, registrationToken *clientapi.RegistrationToken) (bool, error) + PerformAdminListRegistrationTokens(ctx context.Context, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error) PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error diff --git a/userapi/internal/user_api.go b/userapi/internal/user_api.go index 8f388ab82..65ea6a868 100644 --- a/userapi/internal/user_api.go +++ b/userapi/internal/user_api.go @@ -33,6 +33,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/internal/pushgateway" @@ -63,21 +64,29 @@ type UserInternalAPI struct { Updater *DeviceListUpdater } -func (a *UserInternalAPI) PerformAdminCreateRegistrationToken(ctx context.Context, token string, usesAllowed int32, expiryTime int64) (bool, error) { - exists, err := a.DB.RegistrationTokenExists(ctx, token) +func (a *UserInternalAPI) PerformAdminCreateRegistrationToken(ctx context.Context, registrationToken *clientapi.RegistrationToken) (bool, error) { + exists, err := a.DB.RegistrationTokenExists(ctx, *registrationToken.Token) if err != nil { return false, err } if exists { - return false, fmt.Errorf("token: %s already exists", token) + return false, fmt.Errorf("token: %s already exists", *registrationToken.Token) } - _, err = a.DB.InsertRegistrationToken(ctx, token, usesAllowed, expiryTime) + _, err = a.DB.InsertRegistrationToken(ctx, registrationToken) if err != nil { - return false, fmt.Errorf("Error creating token: %s"+err.Error(), token) + return false, fmt.Errorf("Error creating token: %s"+err.Error(), *registrationToken.Token) } return true, nil } +func (a *UserInternalAPI) PerformAdminListRegistrationTokens(ctx context.Context, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error) { + tokens, err := a.DB.ListRegistrationTokens(ctx, returnAll, valid) + if err != nil { + return nil, err + } + return tokens, nil +} + func (a *UserInternalAPI) InputAccountData(ctx context.Context, req *api.InputAccountDataRequest, res *api.InputAccountDataResponse) error { local, domain, err := gomatrixserverlib.SplitID('@', req.UserID) if err != nil { diff --git a/userapi/storage/interface.go b/userapi/storage/interface.go index 8815df68f..986da99b5 100644 --- a/userapi/storage/interface.go +++ b/userapi/storage/interface.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/pushrules" "github.com/matrix-org/dendrite/userapi/api" @@ -32,7 +33,8 @@ import ( type RegistrationTokens interface { RegistrationTokenExists(ctx context.Context, token string) (bool, error) - InsertRegistrationToken(ctx context.Context, token string, usesAllowed int32, expiryTime int64) (bool, error) + InsertRegistrationToken(ctx context.Context, registrationToken *clientapi.RegistrationToken) (bool, error) + ListRegistrationTokens(ctx context.Context, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error) } type Profile interface { diff --git a/userapi/storage/postgres/registration_tokens_table.go b/userapi/storage/postgres/registration_tokens_table.go index 6c55444c0..666fb3c3e 100644 --- a/userapi/storage/postgres/registration_tokens_table.go +++ b/userapi/storage/postgres/registration_tokens_table.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" + "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/userapi/storage/tables" ) @@ -24,9 +25,13 @@ const selectTokenSQL = "" + const insertTokenSQL = "" + "INSERT INTO userapi_registration_tokens (token, uses_allowed, expiry_time, pending, completed) VALUES ($1, $2, $3, $4, $5)" +const listTokensSQL = "" + + "SELECT * FROM userapi_registration_tokens" + type registrationTokenStatements struct { selectTokenStatement *sql.Stmt - insertTokenStatment *sql.Stmt + insertTokenStatement *sql.Stmt + listTokensStatement *sql.Stmt } func NewPostgresRegistrationTokensTable(db *sql.DB) (tables.RegistrationTokensTable, error) { @@ -37,7 +42,8 @@ func NewPostgresRegistrationTokensTable(db *sql.DB) (tables.RegistrationTokensTa } return s, sqlutil.StatementList{ {&s.selectTokenStatement, selectTokenSQL}, - {&s.insertTokenStatment, insertTokenSQL}, + {&s.insertTokenStatement, insertTokenSQL}, + {&s.listTokensStatement, listTokensSQL}, }.Prepare(db) } @@ -54,11 +60,15 @@ func (s *registrationTokenStatements) RegistrationTokenExists(ctx context.Contex return true, nil } -func (s *registrationTokenStatements) InsertRegistrationToken(ctx context.Context, tx *sql.Tx, token string, usesAllowed int32, expiryTime int64) (bool, error) { - stmt := sqlutil.TxStmt(tx, s.insertTokenStatment) - pending := 0 - completed := 0 - _, err := stmt.ExecContext(ctx, token, nullIfZeroInt32(usesAllowed), nullIfZero(expiryTime), pending, completed) +func (s *registrationTokenStatements) InsertRegistrationToken(ctx context.Context, tx *sql.Tx, registrationToken *api.RegistrationToken) (bool, error) { + stmt := sqlutil.TxStmt(tx, s.insertTokenStatement) + _, err := stmt.ExecContext( + ctx, + *registrationToken.Token, + nullIfZeroInt32(*registrationToken.UsesAllowed), + nullIfZero(*registrationToken.ExpiryTime), + *registrationToken.Pending, + *registrationToken.Completed) if err != nil { return false, err } @@ -78,3 +88,53 @@ func nullIfZeroInt32(value int32) interface{} { } return value } + +func (s *registrationTokenStatements) ListRegistrationTokens(ctx context.Context, tx *sql.Tx, returnAll bool, valid bool) ([]api.RegistrationToken, error) { + var stmt *sql.Stmt + var tokens []api.RegistrationToken + var tokenString sql.NullString + var pending, completed, usesAllowed sql.NullInt32 + var expiryTime sql.NullInt64 + if returnAll { + stmt = s.listTokensStatement + } else if valid { + // TODO: Statement to Get All Valid Tokens + } else { + // TODO: Statement to Get All Invalid Tokens + } + rows, err := stmt.QueryContext(ctx) + if err != nil { + return tokens, err + } + for rows.Next() { + err = rows.Scan(&tokenString, &pending, &completed, &usesAllowed, &expiryTime) + if err != nil { + return tokens, err + } + tokenMap := api.RegistrationToken{ + Token: &tokenString.String, + Pending: &pending.Int32, + Completed: &pending.Int32, + UsesAllowed: getReturnValueForInt32(usesAllowed), + ExpiryTime: getReturnValueForInt64(expiryTime), + } + tokens = append(tokens, tokenMap) + } + return tokens, nil +} + +func getReturnValueForInt32(value sql.NullInt32) *int32 { + if value.Valid { + returnValue := value.Int32 + return &returnValue + } + return nil +} + +func getReturnValueForInt64(value sql.NullInt64) *int64 { + if value.Valid { + returnValue := value.Int64 + return &returnValue + } + return nil +} diff --git a/userapi/storage/shared/storage.go b/userapi/storage/shared/storage.go index 9ec210391..58ebe30ec 100644 --- a/userapi/storage/shared/storage.go +++ b/userapi/storage/shared/storage.go @@ -31,6 +31,7 @@ import ( "github.com/matrix-org/gomatrixserverlib/spec" "golang.org/x/crypto/bcrypt" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/pushrules" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -83,14 +84,18 @@ func (d *Database) RegistrationTokenExists(ctx context.Context, token string) (b return d.RegistrationTokens.RegistrationTokenExists(ctx, nil, token) } -func (d *Database) InsertRegistrationToken(ctx context.Context, token string, usesAllowed int32, expiryTime int64) (created bool, err error) { +func (d *Database) InsertRegistrationToken(ctx context.Context, registrationToken *clientapi.RegistrationToken) (created bool, err error) { err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { - created, err = d.RegistrationTokens.InsertRegistrationToken(ctx, txn, token, usesAllowed, expiryTime) + created, err = d.RegistrationTokens.InsertRegistrationToken(ctx, txn, registrationToken) return err }) return } +func (d *Database) ListRegistrationTokens(ctx context.Context, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error) { + return d.RegistrationTokens.ListRegistrationTokens(ctx, nil, returnAll, valid) +} + // GetAccountByPassword returns the account associated with the given localpart and password. // Returns sql.ErrNoRows if no account exists which matches the given localpart. func (d *Database) GetAccountByPassword( diff --git a/userapi/storage/tables/interface.go b/userapi/storage/tables/interface.go index 41c99baed..dfd52235a 100644 --- a/userapi/storage/tables/interface.go +++ b/userapi/storage/tables/interface.go @@ -25,13 +25,15 @@ import ( "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" + clientapi "github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/types" ) type RegistrationTokensTable interface { RegistrationTokenExists(ctx context.Context, txn *sql.Tx, token string) (bool, error) - InsertRegistrationToken(ctx context.Context, txn *sql.Tx, token string, usesAllowed int32, expiryTime int64) (bool, error) + InsertRegistrationToken(ctx context.Context, txn *sql.Tx, registrationToken *clientapi.RegistrationToken) (bool, error) + ListRegistrationTokens(ctx context.Context, txn *sql.Tx, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error) } type AccountDataTable interface {