(w http.ResponseWriter, r *http.Request)
| 1023 | } |
| 1024 | |
| 1025 | func (a *API) UnenrollFactor(w http.ResponseWriter, r *http.Request) error { |
| 1026 | var err error |
| 1027 | ctx := r.Context() |
| 1028 | config := a.config |
| 1029 | user := getUser(ctx) |
| 1030 | factor := getFactor(ctx) |
| 1031 | session := getSession(ctx) |
| 1032 | db := a.db.WithContext(ctx) |
| 1033 | |
| 1034 | if factor == nil || session == nil || user == nil { |
| 1035 | return apierrors.NewInternalServerError("A valid session and factor are required to unenroll a factor") |
| 1036 | } |
| 1037 | |
| 1038 | if factor.IsVerified() && !session.IsAAL2() { |
| 1039 | return apierrors.NewUnprocessableEntityError(apierrors.ErrorCodeInsufficientAAL, "AAL2 required to unenroll verified factor") |
| 1040 | } |
| 1041 | |
| 1042 | factorType := factor.FactorType |
| 1043 | |
| 1044 | err = db.Transaction(func(tx *storage.Connection) error { |
| 1045 | var terr error |
| 1046 | if terr := tx.Destroy(factor); terr != nil { |
| 1047 | return terr |
| 1048 | } |
| 1049 | if terr = models.NewAuditLogEntry(config.AuditLog, r, tx, user, models.UnenrollFactorAction, utilities.GetIPAddress(r), map[string]interface{}{ |
| 1050 | "factor_id": factor.ID, |
| 1051 | "factor_status": factor.Status, |
| 1052 | "session_id": session.ID, |
| 1053 | }); terr != nil { |
| 1054 | return terr |
| 1055 | } |
| 1056 | if terr = factor.DowngradeSessionsToAAL1(tx); terr != nil { |
| 1057 | return terr |
| 1058 | } |
| 1059 | return nil |
| 1060 | }) |
| 1061 | if err != nil { |
| 1062 | return err |
| 1063 | } |
| 1064 | |
| 1065 | // Send MFA factor unenrolled notification email if enabled |
| 1066 | if config.Mailer.Notifications.MFAFactorUnenrolledEnabled && user.GetEmail() != "" { |
| 1067 | if err := a.sendMFAFactorUnenrolledNotification(r, db, user, factorType); err != nil { |
| 1068 | // Log the error but don't fail the unenrollment |
| 1069 | logrus.WithError(err).Warn("Unable to send MFA factor unenrolled notification email") |
| 1070 | } |
| 1071 | } |
| 1072 | |
| 1073 | return sendJSON(w, http.StatusOK, &UnenrollFactorResponse{ |
| 1074 | ID: factor.ID, |
| 1075 | }) |
| 1076 | } |
nothing calls this directly
no test coverage detected