feat(auth): invite links for several people, invite by email, registration with approval or email domains
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m20s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m20s
- Invites: link /?invite=CODE, copy link, send by email, max uses with counter - Registration modes closed | approval | domain | domain+approval | open, waiting list with approve/reject - REGISTRATION_MODE / REGISTRATION_DOMAINS lock the admin setting; ALLOW_REGISTRATION=true is honoured Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db5c5cde9b
commit
af77b9e809
24 files changed
+1077
-142
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/sandrodach/mindsift/internal/auth"
|
||||
"github.com/sandrodach/mindsift/internal/domain"
|
||||
"github.com/sandrodach/mindsift/internal/storage"
|
||||
)
|
||||
|
||||
// --- ORGANIZATIONS & TEAMS ---
|
||||
@@ -93,6 +94,10 @@ func (s *Server) handleAdminGetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
pending := s.store.PendingRegistrationIDs()
|
||||
for i := range users {
|
||||
users[i].PendingApproval = pending[users[i].ID]
|
||||
}
|
||||
writeJSON(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
@@ -191,6 +196,9 @@ func (s *Server) handleAdminUpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
if req.IsActive != nil && !*req.IsActive {
|
||||
s.wsHub.DisconnectUser(targetUserID)
|
||||
}
|
||||
if req.IsActive != nil && *req.IsActive {
|
||||
s.store.ClearPendingRegistration(targetUserID) // activating by hand counts as approval
|
||||
}
|
||||
writeJSON(w, http.StatusOK, u)
|
||||
}
|
||||
|
||||
@@ -247,6 +255,7 @@ func (s *Server) handleAdminDeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
s.removeUserBackups(targetUserID)
|
||||
s.store.ClearPendingRegistration(targetUserID)
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"deleted": true})
|
||||
}
|
||||
|
||||
@@ -270,6 +279,7 @@ func (s *Server) handleAdminCreateInvite(w http.ResponseWriter, r *http.Request)
|
||||
Role string `json:"role"`
|
||||
OrganizationID string `json:"organizationId"`
|
||||
ValidDays int `json:"validDays"`
|
||||
MaxUses int `json:"maxUses"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Ungültiges JSON")
|
||||
@@ -278,7 +288,7 @@ func (s *Server) handleAdminCreateInvite(w http.ResponseWriter, r *http.Request)
|
||||
if req.Role == "admin" && (caller == nil || caller.Role != "admin") {
|
||||
req.Role = "member"
|
||||
}
|
||||
inv, err := s.store.CreateUserInvite(callerID, req.Email, req.Role, req.OrganizationID, req.ValidDays)
|
||||
inv, err := s.store.CreateUserInvite(callerID, req.Email, req.Role, req.OrganizationID, req.ValidDays, req.MaxUses)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
@@ -312,6 +322,16 @@ func (s *Server) settingsForBrowser(settings *domain.SystemSettings) *domain.Sys
|
||||
out.SMTPPassSet = out.SMTPPass != ""
|
||||
out.SMTPPass = ""
|
||||
out.SMTPConfiguredViaEnv = strings.TrimSpace(s.cfg.SMTPHost) != ""
|
||||
// What the environment fixes is shown as in effect and locked in the form
|
||||
if s.cfg.RegistrationMode != "" {
|
||||
out.RegistrationMode = s.cfg.RegistrationMode
|
||||
out.AllowRegistration = out.RegistrationMode == storage.RegistrationOpen
|
||||
out.RegistrationConfiguredViaEnv = true
|
||||
}
|
||||
if s.cfg.RegistrationDomains != "" {
|
||||
out.RegistrationDomains = strings.Join(storage.ParseEmailDomains(s.cfg.RegistrationDomains), ", ")
|
||||
out.RegistrationConfiguredViaEnv = true
|
||||
}
|
||||
return &out
|
||||
}
|
||||
|
||||
@@ -327,6 +347,17 @@ func (s *Server) handleAdminUpdateSettings(w http.ResponseWriter, r *http.Reques
|
||||
settings.SMTPPass = cur.SMTPPass
|
||||
}
|
||||
}
|
||||
// Registration fixed by the environment cannot be changed here (the stored value stays as it was)
|
||||
if s.cfg.RegistrationMode != "" || s.cfg.RegistrationDomains != "" {
|
||||
if cur, err := s.store.GetSystemSettings(); err == nil {
|
||||
if s.cfg.RegistrationMode != "" {
|
||||
settings.RegistrationMode, settings.AllowRegistration = cur.RegistrationMode, cur.AllowRegistration
|
||||
}
|
||||
if s.cfg.RegistrationDomains != "" {
|
||||
settings.RegistrationDomains = cur.RegistrationDomains
|
||||
}
|
||||
}
|
||||
}
|
||||
previousMode := s.store.SharingMode()
|
||||
if err := s.store.UpdateSystemSettings(&settings); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
Reference in new issue
Block a user