Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/pro 1658 sd re authentication #165

Merged
merged 10 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/devices-api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ func startWebAPI(logger zerolog.Logger, settings *config.Settings, pdb db.Store,

sdAuth.Get("/:syntheticDeviceNode/burn", syntheticController.GetSyntheticDeviceBurnPayload)
sdAuth.Post("/:syntheticDeviceNode/burn", syntheticController.BurnSyntheticDevice)

sdAuth.Post("/:syntheticDeviceNode/re-authenticate", syntheticController.ReAuthenticate)
}

// Vehicle owner routes.
Expand Down
24 changes: 23 additions & 1 deletion docs/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,28 @@
}
}
},
"/synthetic/device/{syntheticDeviceNode}/re-authenticate": {
"post": {
"description": "Submit new credentials for the given synthetic device. Mostly\nused in the event of an upstream password change.",
"produces": [
"application/json"
],
"parameters": [
{
"type": "integer",
"description": "synthetic device token id",
"name": "syntheticDeviceNode",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/user/devices": {
"post": {
"security": [
Expand Down
16 changes: 16 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,22 @@ paths:
responses:
"200":
description: OK
/synthetic/device/{syntheticDeviceNode}/re-authenticate:
post:
description: |-
Submit new credentials for the given synthetic device. Mostly
used in the event of an upstream password change.
parameters:
- description: synthetic device token id
in: path
name: syntheticDeviceNode
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
/synthetic/device/mint/{integrationNode}/{vehicleNode}:
get:
description: |-
Expand Down
125 changes: 115 additions & 10 deletions internal/controllers/synthetic_devices_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ type SyntheticDevicesController struct {
usersClient pb.UserServiceClient
walletSvc services.SyntheticWalletInstanceService
registryClient registry.Client

cipher shared.Cipher

smartcarClient services.SmartcarClient
teslaService services.TeslaService
cipher shared.Cipher
smartcarTask services.SmartcarTaskService

teslaService services.TeslaService
teslaTask services.TeslaTaskService
}

type MintSyntheticDeviceRequest struct {
Expand All @@ -55,21 +60,32 @@ type MintSyntheticDeviceRequest struct {
}

type credentials struct {
// Smartcar
Code string `json:"code"`
RedirectURI string `json:"redirectUri"`

// Tesla
ID string `json:"id"`
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
ExpiresIn int64 `json:"expiresIn"`
ExternalID string `json:"externalId"`
Code string `json:"code"`
RedirectURI string `json:"redirectUri"`
}

type SyntheticDeviceSequence struct {
NextVal int `boil:"nextval"`
}

func NewSyntheticDevicesController(
settings *config.Settings, dbs func() *db.ReaderWriter, logger *zerolog.Logger, deviceDefSvc services.DeviceDefinitionService, usersClient pb.UserServiceClient, walletSvc services.SyntheticWalletInstanceService, registryClient registry.Client, smartcarClient services.SmartcarClient, teslaSvc services.TeslaService, cipher shared.Cipher,

settings *config.Settings,
dbs func() *db.ReaderWriter,
logger *zerolog.Logger,
deviceDefSvc services.DeviceDefinitionService,
usersClient pb.UserServiceClient,
walletSvc services.SyntheticWalletInstanceService,
registryClient registry.Client,
smartcarClient services.SmartcarClient,
teslaSvc services.TeslaService,
cipher shared.Cipher,
) SyntheticDevicesController {
return SyntheticDevicesController{
Settings: settings,
Expand Down Expand Up @@ -450,9 +466,9 @@ func (sdc *SyntheticDevicesController) generateUDAI(ctx context.Context, creds c
udi.Metadata = null.JSONFrom(mb)
udi.ExternalID = null.StringFrom(externalID)
case constants.TeslaVendor:
teslaID, err := strconv.Atoi(creds.ExternalID)
teslaID, err := strconv.Atoi(creds.ID)
if err != nil {
sdc.log.Err(err).Msgf("unable to parse external id %q as integer", creds.ExternalID)
sdc.log.Err(err).Msgf("unable to parse external id %q as integer", creds.ID)
return nil, err
}

Expand Down Expand Up @@ -482,7 +498,7 @@ func (sdc *SyntheticDevicesController) generateUDAI(ctx context.Context, creds c

mb, _ := json.Marshal(meta)

udi.ExternalID = null.StringFrom(creds.ExternalID)
udi.ExternalID = null.StringFrom(creds.ID)
udi.AccessExpiresAt = null.TimeFrom(time.Now().Add(time.Duration(creds.ExpiresIn) * time.Second))
udi.Metadata = null.JSONFrom(mb)
default:
Expand Down Expand Up @@ -647,3 +663,92 @@ func (sdc *SyntheticDevicesController) BurnSyntheticDevice(c *fiber.Ctx) error {

return err
}

// ReAuthenticate godoc
// @Description Submit new credentials for the given synthetic device. Mostly
// @Description used in the event of an upstream password change.
// @Produce json
// @Param syntheticDeviceNode path int true "synthetic device token id"
// @Success 200
// @Router /synthetic/device/{syntheticDeviceNode}/re-authenticate [post]
func (sdc *SyntheticDevicesController) ReAuthenticate(c *fiber.Ctx) error {
syntheticDeviceNodeRaw := c.Params("syntheticDeviceNode")
// userID := helpers.GetUserID(c)

sdn, err := strconv.ParseInt(syntheticDeviceNodeRaw, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Couldn't parse the given token id.")
}

sd, err := models.SyntheticDevices(
models.SyntheticDeviceWhere.TokenID.EQ(types.NewNullDecimal(decimal.New(sdn, 0))),
qm.Load(models.SyntheticDeviceRels.VehicleToken),
).One(c.Context(), sdc.DBS().Reader)
if err != nil {
if err == sql.ErrNoRows {
return fiber.NewError(fiber.StatusNotFound, "No synthetic device with that id.")
}
return err
}

if !sd.R.VehicleToken.UserDeviceID.Valid {
return fiber.NewError(fiber.StatusConflict, "Vehicle deleted.")
}

iNode, _ := sd.IntegrationTokenID.Uint64()

i, err := sdc.deviceDefSvc.GetIntegrationByTokenID(c.Context(), iNode)
if err != nil {
return err
}

oldUDAI, err := models.FindUserDeviceAPIIntegration(c.Context(), sdc.DBS().Reader, sd.R.VehicleToken.UserDeviceID.String, i.Id)
if err != nil {
return err
}

// Should really create a new type.
var req MintSyntheticDeviceRequest
if err := c.BodyParser(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Couldn't parse request.")
}

newUDAI, err := sdc.generateUDAI(c.Context(), req.Credentials, sd.R.VehicleToken.UserDeviceID.String, i)
if err != nil {
return err
}

switch i.Vendor {
case constants.SmartCarVendor:
if err := sdc.smartcarTask.StopPoll(oldUDAI); err != nil {
return err
}
if err := sdc.smartcarTask.StartPoll(newUDAI); err != nil {
return err
}
if _, err := newUDAI.Update(c.Context(), sdc.DBS().Writer, boil.Infer()); err != nil {
return err
}
case constants.TeslaVendor:
// Awful
id, err := strconv.Atoi(newUDAI.ExternalID.String)
if err != nil {
return err
}
var md services.UserDeviceAPIIntegrationsMetadata
if err := newUDAI.Metadata.Unmarshal(&md); err != nil {
return err
}
if err := sdc.teslaTask.StopPoll(oldUDAI); err != nil {
return err
}
if err := sdc.teslaTask.StartPoll(&services.TeslaVehicle{ID: id, VehicleID: md.TeslaVehicleID}, newUDAI); err != nil {
return err
}
if _, err := newUDAI.Update(c.Context(), sdc.DBS().Writer, boil.Infer()); err != nil {
return err
}
}

return nil
}
2 changes: 1 addition & 1 deletion internal/controllers/synthetic_devices_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var teslaClientToken = MintSyntheticDeviceRequest{
Credentials: credentials{
AccessToken: "accessToken",
RefreshToken: "refreshToken",
ExternalID: "13",
ID: "13",
ExpiresIn: 28800,
},
}
Expand Down
2 changes: 1 addition & 1 deletion internal/services/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ type UserDeviceAPIIntegrationsMetadata struct {
Commands *UserDeviceAPIIntegrationsMetadataCommands `json:"commands,omitempty"`
// CANProtocol is the protocol that was detected by edge-network from the autopi.
CANProtocol *string `json:"canProtocol,omitempty"`
TeslaVehicleID int `json:"teslaVehicleID,omitempty"`
TeslaVehicleID int `json:"teslaVehicleId,omitempty"`
}

type UserDeviceAPIIntegrationsMetadataCommands struct {
Expand Down
Loading