feat(auth): implement user and device authentication
This commit is contained in:
@@ -29,6 +29,7 @@ type TaskService struct {
|
||||
|
||||
type CreateTaskCommand struct {
|
||||
CreatorSubject string
|
||||
ActorUserID string
|
||||
IdempotencyKey string
|
||||
SourceRef *string
|
||||
Title string
|
||||
@@ -61,6 +62,7 @@ type TaskPage struct {
|
||||
|
||||
type CancelTaskCommand struct {
|
||||
CreatorSubject string
|
||||
ActorUserID string
|
||||
TaskID string
|
||||
Reason string
|
||||
}
|
||||
@@ -91,6 +93,7 @@ func (s *TaskService) Create(
|
||||
return CreateTaskResult{}, err
|
||||
}
|
||||
command.CreatorSubject = strings.TrimSpace(command.CreatorSubject)
|
||||
command.ActorUserID = strings.TrimSpace(command.ActorUserID)
|
||||
command.Title = strings.TrimSpace(command.Title)
|
||||
command.SKU = strings.TrimSpace(command.SKU)
|
||||
command.ImageAssetID = strings.TrimSpace(command.ImageAssetID)
|
||||
@@ -105,6 +108,13 @@ func (s *TaskService) Create(
|
||||
map[string]string{"image_asset_id": "must be a UUID"},
|
||||
)
|
||||
}
|
||||
if !isUUID(command.ActorUserID) {
|
||||
return CreateTaskResult{}, invalidError(
|
||||
"TASK_VALIDATION_FAILED",
|
||||
"task validation failed",
|
||||
map[string]string{"actor_user_id": "must be a UUID"},
|
||||
)
|
||||
}
|
||||
if err := domain.ValidateTaskInput(
|
||||
command.CreatorSubject,
|
||||
command.SourceRef,
|
||||
@@ -156,28 +166,31 @@ func (s *TaskService) Create(
|
||||
)
|
||||
}
|
||||
now := s.clock.Now().UTC()
|
||||
actorUserID := command.ActorUserID
|
||||
task := domain.PurchaseTask{
|
||||
ID: taskID,
|
||||
CreatorSubject: command.CreatorSubject,
|
||||
SourceRef: command.SourceRef,
|
||||
Title: command.Title,
|
||||
Description: command.Description,
|
||||
SKU: command.SKU,
|
||||
ImageAssetID: command.ImageAssetID,
|
||||
Quantity: command.Quantity,
|
||||
MaxBudgetCents: budget,
|
||||
Currency: domain.CurrencyCNY,
|
||||
Status: domain.TaskStatusPending,
|
||||
Version: 1,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ID: taskID,
|
||||
CreatorSubject: command.CreatorSubject,
|
||||
CreatedByUserID: &actorUserID,
|
||||
SourceRef: command.SourceRef,
|
||||
Title: command.Title,
|
||||
Description: command.Description,
|
||||
SKU: command.SKU,
|
||||
ImageAssetID: command.ImageAssetID,
|
||||
Quantity: command.Quantity,
|
||||
MaxBudgetCents: budget,
|
||||
Currency: domain.CurrencyCNY,
|
||||
Status: domain.TaskStatusPending,
|
||||
Version: 1,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
event := domain.TaskEvent{
|
||||
ID: eventID,
|
||||
TaskID: taskID,
|
||||
Type: "TASK_CREATED",
|
||||
Message: "task created",
|
||||
OccurredAt: now,
|
||||
ID: eventID,
|
||||
TaskID: taskID,
|
||||
ActorUserID: &actorUserID,
|
||||
Type: "TASK_CREATED",
|
||||
Message: "task created",
|
||||
OccurredAt: now,
|
||||
}
|
||||
requestHash, err := hashCreateTaskCommand(command, budget)
|
||||
if err != nil {
|
||||
@@ -323,6 +336,7 @@ func (s *TaskService) Cancel(
|
||||
command CancelTaskCommand,
|
||||
) (domain.PurchaseTask, error) {
|
||||
command.CreatorSubject = strings.TrimSpace(command.CreatorSubject)
|
||||
command.ActorUserID = strings.TrimSpace(command.ActorUserID)
|
||||
command.TaskID = strings.TrimSpace(command.TaskID)
|
||||
command.Reason = strings.TrimSpace(command.Reason)
|
||||
fields := make(map[string]string)
|
||||
@@ -332,6 +346,9 @@ func (s *TaskService) Cancel(
|
||||
if !isUUID(command.TaskID) {
|
||||
fields["task_id"] = "must be a UUID"
|
||||
}
|
||||
if !isUUID(command.ActorUserID) {
|
||||
fields["actor_user_id"] = "must be a UUID"
|
||||
}
|
||||
if len([]byte(command.Reason)) > domain.MaxCancelReasonBytes {
|
||||
fields["reason"] = "too long"
|
||||
}
|
||||
@@ -352,12 +369,14 @@ func (s *TaskService) Cancel(
|
||||
)
|
||||
}
|
||||
now := s.clock.Now().UTC()
|
||||
actorUserID := command.ActorUserID
|
||||
event := domain.TaskEvent{
|
||||
ID: eventID,
|
||||
TaskID: command.TaskID,
|
||||
Type: "TASK_CANCELED",
|
||||
Message: "task canceled",
|
||||
OccurredAt: now,
|
||||
ID: eventID,
|
||||
TaskID: command.TaskID,
|
||||
ActorUserID: &actorUserID,
|
||||
Type: "TASK_CANCELED",
|
||||
Message: "task canceled",
|
||||
OccurredAt: now,
|
||||
}
|
||||
task, err := s.repository.CancelPendingTask(
|
||||
ctx,
|
||||
@@ -378,6 +397,7 @@ func hashCreateTaskCommand(
|
||||
budget *int64,
|
||||
) (string, error) {
|
||||
payload := struct {
|
||||
ActorUserID string `json:"actor_user_id"`
|
||||
SourceRef *string `json:"source_ref"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
@@ -386,6 +406,7 @@ func hashCreateTaskCommand(
|
||||
Quantity int `json:"quantity"`
|
||||
MaxBudgetCents *int64 `json:"max_budget_cents"`
|
||||
}{
|
||||
ActorUserID: command.ActorUserID,
|
||||
SourceRef: command.SourceRef,
|
||||
Title: command.Title,
|
||||
Description: command.Description,
|
||||
|
||||
Reference in New Issue
Block a user