Routes to refresh user and channel keys

This commit is contained in:
Mike Schwörer 2022-11-20 21:35:08 +01:00
parent e8671e8650
commit ca58aa782d
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
7 changed files with 387 additions and 31 deletions

View File

@ -3,9 +3,8 @@
//TODO //TODO
- List subscriptions on all owned channels (RESTful?)
- route to re-create keys
- ack/read deliveries && return ack-count - ack/read deliveries && return ack-count
- go ID types
- full-text-search: https://www.sqlite.org/fts5.html#contentless_tables - full-text-search: https://www.sqlite.org/fts5.html#contentless_tables

View File

@ -160,7 +160,13 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
// @Description The body-values are optional, only send the ones you want to update // @Description The body-values are optional, only send the ones you want to update
// @ID api-user-update // @ID api-user-update
// //
// @Param post_body body handler.UpdateUser.body false " " // @Param uid path int true "UserID"
//
// @Param username body string false "Change the username (send an empty string to clear it)"
// @Param pro_token body string false "Send a verification of permium purchase"
// @Param read_key body string false "Send `true` to create a new read_key"
// @Param send_key body string false "Send `true` to create a new send_key"
// @Param admin_key body string false "Send `true` to create a new admin_key"
// //
// @Success 200 {object} models.UserJSON // @Success 200 {object} models.UserJSON
// @Failure 400 {object} ginresp.apiError // @Failure 400 {object} ginresp.apiError
@ -176,6 +182,9 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
type body struct { type body struct {
Username *string `json:"username"` Username *string `json:"username"`
ProToken *string `json:"pro_token"` ProToken *string `json:"pro_token"`
RefreshReadKey *bool `json:"read_key"`
RefreshSendKey *bool `json:"send_key"`
RefreshAdminKey *bool `json:"admin_key"`
} }
var u uri var u uri
@ -223,6 +232,33 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
} }
} }
if langext.Coalesce(b.RefreshSendKey, false) {
newkey := h.app.GenerateRandomAuthKey()
err := h.database.UpdateUserSendKey(ctx, u.UserID, newkey)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
}
}
if langext.Coalesce(b.RefreshReadKey, false) {
newkey := h.app.GenerateRandomAuthKey()
err := h.database.UpdateUserReadKey(ctx, u.UserID, newkey)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
}
}
if langext.Coalesce(b.RefreshAdminKey, false) {
newkey := h.app.GenerateRandomAuthKey()
err := h.database.UpdateUserAdminKey(ctx, u.UserID, newkey)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
}
}
user, err := h.database.GetUser(ctx, u.UserID) user, err := h.database.GetUser(ctx, u.UserID)
if err != nil { if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err)
@ -549,6 +585,80 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true))) return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true)))
} }
// UpdateChannel swaggerdoc
//
// @Summary (Partially) update a channel
// @ID api-channels-update
//
// @Param uid path int true "UserID"
// @Param cid path int true "ChannelID"
//
// @Param subscribe_key body string false "Send `true` to create a new subscribe_key"
// @Param send_key body string false "Send `true` to create a new send_key"
//
// @Success 200 {object} models.ChannelJSON
// @Failure 400 {object} ginresp.apiError
// @Failure 401 {object} ginresp.apiError
// @Failure 404 {object} ginresp.apiError
// @Failure 500 {object} ginresp.apiError
//
// @Router /api-v2/users/{uid}/channels/{cid} [PATCH]
func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
UserID int64 `uri:"uid"`
ChannelID int64 `uri:"cid"`
}
type body struct {
RefreshSubscribeKey *bool `json:"subscribe_key"`
RefreshSendKey *bool `json:"send_key"`
}
var u uri
var b body
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
return *permResp
}
_, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
if err == sql.ErrNoRows {
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
}
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
}
if langext.Coalesce(b.RefreshSendKey, false) {
newkey := h.app.GenerateRandomAuthKey()
err := h.database.UpdateChannelSendKey(ctx, u.ChannelID, newkey)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
}
}
if langext.Coalesce(b.RefreshSubscribeKey, false) {
newkey := h.app.GenerateRandomAuthKey()
err := h.database.UpdateChannelSubscribeKey(ctx, u.ChannelID, newkey)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
}
}
user, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err)
}
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, user.JSON(true)))
}
// ListChannelMessages swaggerdoc // ListChannelMessages swaggerdoc
// //
// @Summary List messages of a channel // @Summary List messages of a channel

View File

@ -105,6 +105,7 @@ func (r *Router) Init(e *gin.Engine) {
apiv2.GET("/users/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels)) apiv2.GET("/users/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels))
apiv2.GET("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel)) apiv2.GET("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel))
apiv2.PATCH("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.UpdateChannel))
apiv2.GET("/users/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.ListChannelMessages)) apiv2.GET("/users/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.ListChannelMessages))
apiv2.GET("/users/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions)) apiv2.GET("/users/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions))

View File

@ -167,3 +167,35 @@ func (db *Database) IncChannelMessageCounter(ctx TxContext, channel models.Chann
return nil return nil
} }
func (db *Database) UpdateChannelSendKey(ctx TxContext, channelid int64, newkey string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "UPDATE channels SET send_key = ? WHERE channel_id = ?",
newkey,
channelid)
if err != nil {
return err
}
return nil
}
func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid int64, newkey string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "UPDATE channels SET subscribe_key = ? WHERE channel_id = ?",
newkey,
channelid)
if err != nil {
return err
}
return nil
}

View File

@ -191,3 +191,51 @@ func (db *Database) UpdateUserKeys(ctx TxContext, userid int64, sendKey string,
return nil return nil
} }
func (db *Database) UpdateUserSendKey(ctx TxContext, userid int64, newkey string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "UPDATE users SET send_key = ? WHERE user_id = ?",
newkey,
userid)
if err != nil {
return err
}
return nil
}
func (db *Database) UpdateUserReadKey(ctx TxContext, userid int64, newkey string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "UPDATE users SET read_key = ? WHERE user_id = ?",
newkey,
userid)
if err != nil {
return err
}
return nil
}
func (db *Database) UpdateUserAdminKey(ctx TxContext, userid int64, newkey string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, "UPDATE users SET admin_key = ? WHERE user_id = ?",
newkey,
userid)
if err != nil {
return err
}
return nil
}

View File

@ -424,11 +424,50 @@
"operationId": "api-user-update", "operationId": "api-user-update",
"parameters": [ "parameters": [
{ {
"description": " ", "type": "integer",
"name": "post_body", "description": "UserID",
"name": "uid",
"in": "path",
"required": true
},
{
"description": "Change the username (send an empty string to clear it)",
"name": "username",
"in": "body", "in": "body",
"schema": { "schema": {
"$ref": "#/definitions/handler.UpdateUser.body" "type": "string"
}
},
{
"description": "Send a verification of permium purchase",
"name": "pro_token",
"in": "body",
"schema": {
"type": "string"
}
},
{
"description": "Send `true` to create a new read_key",
"name": "read_key",
"in": "body",
"schema": {
"type": "string"
}
},
{
"description": "Send `true` to create a new send_key",
"name": "send_key",
"in": "body",
"schema": {
"type": "string"
}
},
{
"description": "Send `true` to create a new admin_key",
"name": "admin_key",
"in": "body",
"schema": {
"type": "string"
} }
} }
], ],
@ -469,7 +508,7 @@
"/api-v2/users/{uid}/channels": { "/api-v2/users/{uid}/channels": {
"get": { "get": {
"description": "The possible values for 'selector' are:\n- \"owned\" Return all channels of the user\n- \"subscribed\" Return all channels that the user is subscribing to\n- \"all\" Return channels that the user owns or is subscribing\n- \"subscribed_any\" Return all channels that the user is subscribing to (even unconfirmed)\n- \"all_any\" Return channels that the user owns or is subscribing (even unconfirmed)", "description": "The possible values for 'selector' are:\n- \"owned\" Return all channels of the user\n- \"subscribed\" Return all channels that the user is subscribing to\n- \"all\" Return channels that the user owns or is subscribing\n- \"subscribed_any\" Return all channels that the user is subscribing to (even unconfirmed)\n- \"all_any\" Return channels that the user owns or is subscribing (even unconfirmed)",
"summary": "List all channels of a user", "summary": "List channels of a user (subscribed/owned)",
"operationId": "api-channels-list", "operationId": "api-channels-list",
"parameters": [ "parameters": [
{ {
@ -580,6 +619,74 @@
} }
} }
} }
},
"patch": {
"summary": "(Partially) update a channel",
"operationId": "api-channels-update",
"parameters": [
{
"type": "integer",
"description": "UserID",
"name": "uid",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "ChannelID",
"name": "cid",
"in": "path",
"required": true
},
{
"description": "Send `true` to create a new subscribe_key",
"name": "subscribe_key",
"in": "body",
"schema": {
"type": "string"
}
},
{
"description": "Send `true` to create a new send_key",
"name": "send_key",
"in": "body",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.ChannelJSON"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
}
}
} }
}, },
"/api-v2/users/{uid}/channels/{cid}/messages": { "/api-v2/users/{uid}/channels/{cid}/messages": {
@ -2283,17 +2390,6 @@
} }
} }
}, },
"handler.UpdateUser.body": {
"type": "object",
"properties": {
"pro_token": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"handler.Upgrade.response": { "handler.Upgrade.response": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2404,6 +2500,10 @@
"owner_user_id": { "owner_user_id": {
"type": "integer" "type": "integer"
}, },
"send_key": {
"description": "can be nil, depending on endpoint",
"type": "string"
},
"subscribe_key": { "subscribe_key": {
"description": "can be nil, depending on endpoint", "description": "can be nil, depending on endpoint",
"type": "string" "type": "string"

View File

@ -245,13 +245,6 @@ definitions:
user_key: user_key:
type: string type: string
type: object type: object
handler.UpdateUser.body:
properties:
pro_token:
type: string
username:
type: string
type: object
handler.Upgrade.response: handler.Upgrade.response:
properties: properties:
is_pro: is_pro:
@ -324,6 +317,9 @@ definitions:
type: string type: string
owner_user_id: owner_user_id:
type: integer type: integer
send_key:
description: can be nil, depending on endpoint
type: string
subscribe_key: subscribe_key:
description: can be nil, depending on endpoint description: can be nil, depending on endpoint
type: string type: string
@ -715,11 +711,36 @@ paths:
description: The body-values are optional, only send the ones you want to update description: The body-values are optional, only send the ones you want to update
operationId: api-user-update operationId: api-user-update
parameters: parameters:
- description: ' ' - description: UserID
in: path
name: uid
required: true
type: integer
- description: Change the username (send an empty string to clear it)
in: body in: body
name: post_body name: username
schema: schema:
$ref: '#/definitions/handler.UpdateUser.body' type: string
- description: Send a verification of permium purchase
in: body
name: pro_token
schema:
type: string
- description: Send `true` to create a new read_key
in: body
name: read_key
schema:
type: string
- description: Send `true` to create a new send_key
in: body
name: send_key
schema:
type: string
- description: Send `true` to create a new admin_key
in: body
name: admin_key
schema:
type: string
responses: responses:
"200": "200":
description: OK description: OK
@ -790,7 +811,7 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
summary: List all channels of a user summary: List channels of a user (subscribed/owned)
/api-v2/users/{uid}/channels/{cid}: /api-v2/users/{uid}/channels/{cid}:
get: get:
operationId: api-channels-get operationId: api-channels-get
@ -827,6 +848,51 @@ paths:
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
summary: List all channels of a user summary: List all channels of a user
patch:
operationId: api-channels-update
parameters:
- description: UserID
in: path
name: uid
required: true
type: integer
- description: ChannelID
in: path
name: cid
required: true
type: integer
- description: Send `true` to create a new subscribe_key
in: body
name: subscribe_key
schema:
type: string
- description: Send `true` to create a new send_key
in: body
name: send_key
schema:
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.ChannelJSON'
"400":
description: Bad Request
schema:
$ref: '#/definitions/ginresp.apiError'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/ginresp.apiError'
"404":
description: Not Found
schema:
$ref: '#/definitions/ginresp.apiError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/ginresp.apiError'
summary: (Partially) update a channel
/api-v2/users/{uid}/channels/{cid}/messages: /api-v2/users/{uid}/channels/{cid}/messages:
get: get:
description: |- description: |-