mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00

- Added struct for oauth - Added interception handler for Zoraxy SSO - Added user structure for SSO
310 lines
7.2 KiB
Go
310 lines
7.2 KiB
Go
package sso
|
|
|
|
/*
|
|
userHandlers.go
|
|
Handlers for SSO user management
|
|
|
|
If you are looking for handlers that changes the settings
|
|
of the SSO portal (e.g. authURL or port), please refer to
|
|
handlers.go.
|
|
*/
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"imuslab.com/zoraxy/mod/auth"
|
|
"imuslab.com/zoraxy/mod/utils"
|
|
)
|
|
|
|
// HandleAddUser handle the request to add a new user to the SSO system
|
|
func (s *SSOHandler) HandleAddUser(w http.ResponseWriter, r *http.Request) {
|
|
username, err := utils.PostPara(r, "username")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid username given")
|
|
return
|
|
}
|
|
|
|
password, err := utils.PostPara(r, "password")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid password given")
|
|
return
|
|
}
|
|
|
|
newUserId, err := uuid.NewV4()
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to generate new user ID")
|
|
return
|
|
}
|
|
|
|
//Create a new user entry
|
|
thisUserEntry := UserEntry{
|
|
UserID: newUserId.String(),
|
|
Username: username,
|
|
PasswordHash: auth.Hash(password),
|
|
TOTPCode: "",
|
|
Enable2FA: false,
|
|
}
|
|
|
|
js, _ := json.Marshal(thisUserEntry)
|
|
|
|
//Create a new user in the database
|
|
err = s.Config.Database.Write("sso_users", newUserId.String(), string(js))
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to create new user")
|
|
return
|
|
}
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// Edit user information, only accept change of username, password and enabled subdomain filed
|
|
func (s *SSOHandler) HandleEditUser(w http.ResponseWriter, r *http.Request) {
|
|
userID, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userID)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
//Load the user entry from database
|
|
userEntry, err := s.GetSSOUser(userID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return
|
|
}
|
|
|
|
//Update each of the fields if it is provided
|
|
username, err := utils.PostPara(r, "username")
|
|
if err == nil {
|
|
userEntry.Username = username
|
|
}
|
|
|
|
password, err := utils.PostPara(r, "password")
|
|
if err == nil {
|
|
userEntry.PasswordHash = auth.Hash(password)
|
|
}
|
|
|
|
//Update the user entry in the database
|
|
js, _ := json.Marshal(userEntry)
|
|
err = s.Config.Database.Write("sso_users", userID, string(js))
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to update user entry")
|
|
return
|
|
}
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// HandleRemoveUser remove a user from the SSO system
|
|
func (s *SSOHandler) HandleRemoveUser(w http.ResponseWriter, r *http.Request) {
|
|
userID, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userID)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
//Remove the user from the database
|
|
err = s.Config.Database.Delete("sso_users", userID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to remove user")
|
|
return
|
|
}
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// HandleListUser list all users in the SSO system
|
|
func (s *SSOHandler) HandleListUser(w http.ResponseWriter, r *http.Request) {
|
|
ssoUsers, err := s.ListSSOUsers()
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to list users")
|
|
return
|
|
}
|
|
js, _ := json.Marshal(ssoUsers)
|
|
utils.SendJSONResponse(w, string(js))
|
|
}
|
|
|
|
// HandleAddSubdomain add a subdomain to a user
|
|
func (s *SSOHandler) HandleAddSubdomain(w http.ResponseWriter, r *http.Request) {
|
|
userid, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userid)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
UserEntry, err := s.GetSSOUser(userid)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return
|
|
}
|
|
|
|
subdomain, err := utils.PostPara(r, "subdomain")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid subdomain given")
|
|
return
|
|
}
|
|
|
|
allowAccess, err := utils.PostBool(r, "allow_access")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid allow access value given")
|
|
return
|
|
}
|
|
|
|
UserEntry.Subdomains[subdomain] = &SubdomainAccessRule{
|
|
Subdomain: subdomain,
|
|
AllowAccess: allowAccess,
|
|
}
|
|
|
|
err = UserEntry.Update()
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to update user entry")
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// HandleRemoveSubdomain remove a subdomain from a user
|
|
func (s *SSOHandler) HandleRemoveSubdomain(w http.ResponseWriter, r *http.Request) {
|
|
userid, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userid)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
UserEntry, err := s.GetSSOUser(userid)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return
|
|
}
|
|
|
|
subdomain, err := utils.PostPara(r, "subdomain")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid subdomain given")
|
|
return
|
|
}
|
|
|
|
delete(UserEntry.Subdomains, subdomain)
|
|
|
|
err = UserEntry.Update()
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to update user entry")
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// HandleEnable2FA enable 2FA for a user
|
|
func (s *SSOHandler) HandleEnable2FA(w http.ResponseWriter, r *http.Request) {
|
|
userid, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userid)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
UserEntry, err := s.GetSSOUser(userid)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return
|
|
}
|
|
|
|
UserEntry.Enable2FA = true
|
|
provisionUri, err := UserEntry.ResetTotp(UserEntry.UserID, "Zoraxy-SSO")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to reset TOTP")
|
|
return
|
|
}
|
|
//As the ResetTotp function will update the user entry in the database, no need to call Update here
|
|
|
|
js, _ := json.Marshal(provisionUri)
|
|
utils.SendJSONResponse(w, string(js))
|
|
}
|
|
|
|
// Handle Disable 2FA for a user
|
|
func (s *SSOHandler) HandleDisable2FA(w http.ResponseWriter, r *http.Request) {
|
|
userid, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid user ID given")
|
|
return
|
|
}
|
|
|
|
if !(s.SSOUserExists(userid)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return
|
|
}
|
|
|
|
UserEntry, err := s.GetSSOUser(userid)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return
|
|
}
|
|
|
|
UserEntry.Enable2FA = false
|
|
UserEntry.TOTPCode = ""
|
|
|
|
err = UserEntry.Update()
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to update user entry")
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// HandleVerify2FA verify the 2FA code for a user
|
|
func (s *SSOHandler) HandleVerify2FA(w http.ResponseWriter, r *http.Request) (bool, error) {
|
|
userid, err := utils.PostPara(r, "user_id")
|
|
if err != nil {
|
|
return false, errors.New("invalid user ID given")
|
|
}
|
|
|
|
if !(s.SSOUserExists(userid)) {
|
|
utils.SendErrorResponse(w, "user not found")
|
|
return false, errors.New("user not found")
|
|
}
|
|
|
|
UserEntry, err := s.GetSSOUser(userid)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "failed to load user entry")
|
|
return false, errors.New("failed to load user entry")
|
|
}
|
|
|
|
totpCode, _ := utils.PostPara(r, "totp_code")
|
|
|
|
if !UserEntry.Enable2FA {
|
|
//If 2FA is not enabled, return true
|
|
return true, nil
|
|
}
|
|
|
|
if !UserEntry.VerifyTotp(totpCode) {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|