Skip to content
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
35 changes: 25 additions & 10 deletions google/api/group_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ func CreateGoogleBinding(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "google_group_email must be a valid email address"})
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Minute)
ctx, cancel := context.WithTimeout(c.Request.Context(), 25*time.Second)
defer cancel()
binding, preflight, err := service.ApplyGoogleBinding(
binding, preflight, err := service.QueueGoogleBinding(
ctx,
req.GroupID,
email,
Expand All @@ -73,7 +73,7 @@ func CreateGoogleBinding(c *gin.Context) {
writeGoogleBindingError(c, err, preflight)
return
}
c.JSON(http.StatusOK, binding)
writeGoogleBindingQueued(c, binding)
}

type googleBindingPreflightRequest struct {
Expand All @@ -93,7 +93,7 @@ func PreflightGoogleBinding(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "google_group_email must be a valid email address"})
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
ctx, cancel := context.WithTimeout(c.Request.Context(), 25*time.Second)
defer cancel()
preflight, err := service.PreflightGoogleBinding(ctx, req.GroupID, email)
if err != nil {
Expand Down Expand Up @@ -123,9 +123,9 @@ func ApplyGoogleBinding(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "google_group_email must be a valid email address"})
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Minute)
ctx, cancel := context.WithTimeout(c.Request.Context(), 25*time.Second)
defer cancel()
binding, preflight, err := service.ApplyGoogleBinding(
binding, preflight, err := service.QueueGoogleBinding(
ctx,
req.GroupID,
email,
Expand All @@ -139,7 +139,7 @@ func ApplyGoogleBinding(c *gin.Context) {
writeGoogleBindingError(c, err, preflight)
return
}
c.JSON(http.StatusOK, gin.H{"binding": binding})
writeGoogleBindingQueued(c, binding)
}

func normalizeGoogleGroupEmail(value string, allowEmpty bool) (string, error) {
Expand All @@ -163,13 +163,28 @@ func writeGoogleBindingError(c *gin.Context, err error, preflight service.Google
c.JSON(http.StatusConflict, gin.H{"error": err.Error(), "preflight": preflight})
case errors.As(err, &alreadyBoundErr):
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
case errors.Is(err, service.ErrBindingSyncInProgress):
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
case errors.Is(err, service.ErrGoogleSyncUnavailable):
c.JSON(http.StatusServiceUnavailable, gin.H{"error": err.Error()})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
}

func writeGoogleBindingQueued(c *gin.Context, binding *model.GroupGoogleBinding) {
if binding == nil || binding.OperationID == "" {
c.JSON(http.StatusOK, gin.H{"binding": binding, "status": "unchanged"})
return
}
c.JSON(http.StatusAccepted, gin.H{
"binding": binding,
"message": "google group sync queued",
"operation_id": binding.OperationID,
"status": "queued",
})
}

// DeleteGoogleBinding removes a binding by ID. The group_id query param is
// required to scope the delete — protects against URL tampering that would
// otherwise let a caller delete a binding for a group they don't control.
Expand All @@ -181,9 +196,9 @@ func DeleteGoogleBinding(c *gin.Context) {
return
}
Require(c, RequestTokenHasAdminAccess(c))
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Minute)
ctx, cancel := context.WithTimeout(c.Request.Context(), 25*time.Second)
defer cancel()
_, preflight, err := service.ApplyGoogleBinding(
binding, preflight, err := service.QueueGoogleBinding(
ctx,
groupID,
"",
Expand All @@ -196,5 +211,5 @@ func DeleteGoogleBinding(c *gin.Context) {
writeGoogleBindingError(c, err, preflight)
return
}
c.Status(http.StatusNoContent)
writeGoogleBindingQueued(c, binding)
}
17 changes: 13 additions & 4 deletions google/model/group_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ import "time"
// reference Sentinel group IDs from core but live in google's domain. Sync is
// one-way (Sentinel -> Google); this row only records where to project.
type GroupGoogleBinding struct {
ID string `json:"id" gorm:"primaryKey"`
GroupID string `json:"group_id" gorm:"uniqueIndex"`
GoogleGroupEmail string `json:"google_group_email" gorm:"uniqueIndex"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
ID string `json:"id" gorm:"primaryKey"`
GroupID string `json:"group_id" gorm:"uniqueIndex"`
GoogleGroupEmail string `json:"google_group_email" gorm:"uniqueIndex"`
Status string `json:"status" gorm:"default:active;index"`
OperationID string `json:"operation_id,omitempty" gorm:"index"`
PreviousGoogleGroupEmail string `json:"-"`
PreviousGoogleGroupID string `json:"-"`
TargetGoogleGroupID string `json:"-"`
DeleteRequested bool `json:"-"`
AllowBulkRemovals bool `json:"-"`
LastSyncError string `json:"last_sync_error,omitempty"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}

func (GroupGoogleBinding) TableName() string {
Expand Down
Loading
Loading