package barcode import ( "fmt" "net/http" "strings" "edge-infra.dev/pkg/edge/iam/apperror" "edge-infra.dev/pkg/edge/iam/config" "edge-infra.dev/pkg/edge/iam/device" "edge-infra.dev/pkg/edge/iam/profile" "edge-infra.dev/pkg/edge/iam/prometheus" "edge-infra.dev/pkg/edge/iam/util" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/gorilla/sessions" ) type emergencyBarcodeRequest struct { Username string `form:"username"` Reason string `form:"reason"` } type Emergency struct { profileStorage profile.Storage sessionStore sessions.Store deviceStorage device.Storage } func NewEmergency(router *gin.Engine, sessionStore sessions.Store, storage interface{}, _ *prometheus.Metrics) *Emergency { emergency := &Emergency{ profileStorage: storage.(profile.Storage), sessionStore: sessionStore, deviceStorage: storage.(device.Storage), } router.POST("/idp/handle-emergency", util.MakeHandlerFunc(emergency.handleEBCRequest)) return emergency } func (emergency *Emergency) handleEBCRequest(c *gin.Context) error { var form emergencyBarcodeRequest if err := c.ShouldBind(&form); err != nil { return apperror.NewAbortError( fmt.Errorf("invalid emergency barcode request: %w", err), http.StatusBadRequest) } form.Username = strings.ToLower(form.Username) session, _ := emergency.sessionStore.Get(c.Request, "oauth2") switch form.Reason { case "11": session.Values["reason"] = form.Reason default: var subject string if config.DeviceLoginEnabled() { acc, err := emergency.deviceStorage.GetDeviceAccount(c, form.Username) if err != nil { return apperror.NewAbortError( fmt.Errorf("failed to get a device account: %w", err), http.StatusInternalServerError) } if acc == nil { // there is no profile for this subject in db return apperror.NewAbortError( fmt.Errorf("device account not found (devicelogin=`%v`): %w", form.Username, err), http.StatusBadRequest) } subject = acc.Subject } else { subject = form.Username } userProfile, err := emergency.profileStorage.GetIdentityProfile(c, subject) if err != nil { return apperror.NewAbortError( fmt.Errorf("failed to get user profile: %w", err), http.StatusInternalServerError) } if userProfile == nil { // there is no profile for this subject in db return apperror.NewAbortError( fmt.Errorf("user profile not found (subject=`%v`): %w", form.Username, err), http.StatusBadRequest) } // set to IssueEmergencyBarcode, to being issuing EBC in continuation. session.Values["reason"] = "10" // set issued by in session, to ensure clarity & readability of code. session.Values["iby"] = session.Values["alias"] session.Values["sub"] = userProfile.Alias // set roles of user to which EBC is being issued as ebc-rls, so we can verify permissions later. session.Values["ebc-rls"] = userProfile.Roles if userProfile.Alias == "" { err := emergency.profileStorage.AddAliasToProfile(c, userProfile) if err != nil { return apperror.NewAbortError( fmt.Errorf("failed to add alias to profile: %w", err), http.StatusInternalServerError) } } } continuation := uuid.New().String() session.Values["continuation"] = continuation err := session.Save(c.Request, c.Writer) if err != nil { return apperror.NewAbortError( fmt.Errorf("failed to save cookie session: %w", err), http.StatusInternalServerError) } return util.WriteJSON(c.Writer, http.StatusOK, gin.H{ "challenge": continuation, }) }