From 4404929c90de86c8ec690ddfe5f0e985c2ac47b3 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Wed, 26 Feb 2025 22:26:13 +0100 Subject: [PATCH] added API endpoints to set/remove a pin see #660 --- src/api/api.go | 69 +++++++++++++++++++++++++++++++++++++++++++ src/client/client.go | 46 +++++++++++++++++++++++++++++ src/docs/docs.go | 68 +++++++++++++++++++++++++++++++++++++++++- src/docs/swagger.json | 68 +++++++++++++++++++++++++++++++++++++++++- src/docs/swagger.yaml | 44 +++++++++++++++++++++++++++ src/main.go | 2 ++ 6 files changed, 295 insertions(+), 2 deletions(-) diff --git a/src/api/api.go b/src/api/api.go index 738bb6e..5e78449 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -202,6 +202,10 @@ type AddStickerPackRequest struct { PackKey string `json:"pack_key" example:"19546e18eba0ff69dea78eb591465289d39e16f35e58389ae779d4f9455aff3a"` } +type SetPinRequest struct { + Pin string `json:"pin"` +} + type Api struct { signalClient *client.SignalClient wsMutex sync.Mutex @@ -2166,3 +2170,68 @@ func (a *Api) ListContacts(c *gin.Context) { c.JSON(200, contacts) } + +// @Summary Set Pin +// @Tags Accounts +// @Description Sets a new Signal Pin +// @Produce json +// @Success 201 +// @Failure 400 {object} Error +// @Param number path string true "Registered Phone Number" +// @Router /v1/accounts/{number}/pin [get] +func (a *Api) SetPin(c *gin.Context) { + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } + + if number == "" { + c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) + return + } + + var req SetPinRequest + err = c.BindJSON(&req) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) + return + } + + err = a.signalClient.SetPin(number, req.Pin) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + + c.Status(201) +} + +// @Summary Remove Pin +// @Tags Accounts +// @Description Removes a Signal Pin +// @Produce json +// @Success 204 +// @Failure 400 {object} Error +// @Param number path string true "Registered Phone Number" +// @Router /v1/accounts/{number}/pin [delete] +func (a *Api) RemovePin(c *gin.Context) { + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } + + if number == "" { + c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) + return + } + + err = a.signalClient.RemovePin(number) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + + c.Status(204) +} diff --git a/src/client/client.go b/src/client/client.go index 02e1365..cc45eb5 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -2239,3 +2239,49 @@ func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, erro return resp, nil } + +func (s *SignalClient) SetPin(number string, registrationLockPin string) (error) { + if s.signalCliMode == JsonRpc { + type Request struct { + RegistrationLockPin string `json:"pin"` + } + req := Request{RegistrationLockPin: registrationLockPin} + jsonRpc2Client, err := s.getJsonRpc2Client() + if err != nil { + return err + } + _, err = jsonRpc2Client.getRaw("setPin", &number, req) + if err != nil { + return err + } + } else { + cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "setPin", registrationLockPin} + rawData, err := s.cliClient.Execute(true, cmd, "") + if err != nil { + return err + } + log.Info(string(rawData)) + } + return nil +} + + +func (s *SignalClient) RemovePin(number string) (error) { + if s.signalCliMode == JsonRpc { + jsonRpc2Client, err := s.getJsonRpc2Client() + if err != nil { + return err + } + _, err = jsonRpc2Client.getRaw("removePin", &number, nil) + if err != nil { + return err + } + } else { + cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "removePin"} + _, err := s.cliClient.Execute(true, cmd, "") + if err != nil { + return err + } + } + return nil +} diff --git a/src/docs/docs.go b/src/docs/docs.go index 2f9fa85..6ad2272 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -64,6 +64,68 @@ const docTemplate = `{ } } }, + "/v1/accounts/{number}/pin": { + "get": { + "description": "Sets a new Signal Pin", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Set Pin", + "parameters": [ + { + "type": "string", + "description": "Registered Phone Number", + "name": "number", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "description": "Removes a Signal Pin", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Remove Pin", + "parameters": [ + { + "type": "string", + "description": "Registered Phone Number", + "name": "number", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/accounts/{number}/rate-limit-challenge": { "post": { "description": "When running into rate limits, sometimes the limit can be lifted, by solving a CAPTCHA. To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html For the staging environment, use: https://signalcaptchas.org/staging/registration/generate.html. The \"challenge_token\" is the token from the failed send attempt. The \"captcha\" is the captcha result, starting with signalcaptcha://", @@ -1717,7 +1779,8 @@ const docTemplate = `{ "type": "string", "description": "Registered Phone Number", "name": "number", - "in": "path" + "in": "path", + "required": true }, { "type": "array", @@ -2526,6 +2589,9 @@ const docTemplate = `{ "blocked": { "type": "boolean" }, + "description": { + "type": "string" + }, "id": { "type": "string" }, diff --git a/src/docs/swagger.json b/src/docs/swagger.json index e55d097..7288e73 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -61,6 +61,68 @@ } } }, + "/v1/accounts/{number}/pin": { + "get": { + "description": "Sets a new Signal Pin", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Set Pin", + "parameters": [ + { + "type": "string", + "description": "Registered Phone Number", + "name": "number", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "description": "Removes a Signal Pin", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Remove Pin", + "parameters": [ + { + "type": "string", + "description": "Registered Phone Number", + "name": "number", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/accounts/{number}/rate-limit-challenge": { "post": { "description": "When running into rate limits, sometimes the limit can be lifted, by solving a CAPTCHA. To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html For the staging environment, use: https://signalcaptchas.org/staging/registration/generate.html. The \"challenge_token\" is the token from the failed send attempt. The \"captcha\" is the captcha result, starting with signalcaptcha://", @@ -1714,7 +1776,8 @@ "type": "string", "description": "Registered Phone Number", "name": "number", - "in": "path" + "in": "path", + "required": true }, { "type": "array", @@ -2523,6 +2586,9 @@ "blocked": { "type": "boolean" }, + "description": { + "type": "string" + }, "id": { "type": "string" }, diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 448f585..288200b 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -312,6 +312,8 @@ definitions: type: array blocked: type: boolean + description: + type: string id: type: string internal_id: @@ -433,6 +435,47 @@ paths: summary: List all accounts tags: - Accounts + /v1/accounts/{number}/pin: + delete: + description: Removes a Signal Pin + parameters: + - description: Registered Phone Number + in: path + name: number + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: Remove Pin + tags: + - Accounts + get: + description: Sets a new Signal Pin + parameters: + - description: Registered Phone Number + in: path + name: number + required: true + type: string + produces: + - application/json + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: Set Pin + tags: + - Accounts /v1/accounts/{number}/rate-limit-challenge: post: consumes: @@ -1544,6 +1587,7 @@ paths: - description: Registered Phone Number in: path name: number + required: true type: string - collectionFormat: multi description: Numbers to check diff --git a/src/main.go b/src/main.go index 6b5cce8..70327a2 100644 --- a/src/main.go +++ b/src/main.go @@ -223,6 +223,8 @@ func main() { accounts.PUT(":number/settings", api.UpdateAccountSettings) accounts.POST(":number/username", api.SetUsername) accounts.DELETE(":number/username", api.RemoveUsername) + accounts.POST(":number/pin", api.SetPin) + accounts.DELETE(":number/pin", api.RemovePin) } devices := v1.Group("devices")