mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00

* chore: update auth * chore: password changes * chore: make changes in oss code * chore: login * chore: get to a running state * fix: migration inital commit * fix: signoz cloud intgtn tests * fix: minor fixes * chore: sso code fixed with org domain * fix: tests * fix: ee auth api's * fix: changes in name * fix: return user in login api * fix: address comments * fix: validate password * fix: handle get domain by email properly * fix: move authomain to usermodule * fix: use displayname instead of hname * fix: rename back endpoints * fix: update telemetry * fix: correct errors * fix: test and fix the invite endpoints * fix: delete all things related to user in store * fix: address issues * fix: ee delete invite * fix: rename func * fix: update user and update role * fix: update role * fix: login and invite changes * fix: return org name in users response * fix: update user role * fix: nil check * fix: getinvite and update role * fix: sso * fix: getinvite use sso ctx * fix: use correct sourceurl * fix: getsourceurl from req payload * fix: update created_at * fix: fix reset password * fix: sso signup and token password change * fix: don't delete last admin * fix: reset password and migration * fix: migration * fix: reset password for sso users * fix: clean up invite * fix: migration * fix: update claims and store code * fix: use correct error * fix: proper nil checks * fix: make migration multitenant * fix: address comments * fix: minor fixes * fix: test * fix: rename reset password --------- Co-authored-by: Vikrant Gupta <vikrant@signoz.io>
192 lines
6.6 KiB
Go
192 lines
6.6 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/SigNoz/signoz/ee/query-service/constants"
|
|
"github.com/SigNoz/signoz/ee/query-service/model"
|
|
"github.com/SigNoz/signoz/pkg/http/render"
|
|
)
|
|
|
|
func parseRequest(r *http.Request, req interface{}) error {
|
|
defer r.Body.Close()
|
|
requestBody, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(requestBody, &req)
|
|
return err
|
|
}
|
|
|
|
// loginUser overrides base handler and considers SSO case.
|
|
func (ah *APIHandler) loginUser(w http.ResponseWriter, r *http.Request) {
|
|
r, err := ah.updateRequestContext(w, r)
|
|
if err != nil {
|
|
render.Error(w, err)
|
|
return
|
|
}
|
|
ah.Signoz.Handlers.User.Login(w, r)
|
|
return
|
|
}
|
|
|
|
func handleSsoError(w http.ResponseWriter, r *http.Request, redirectURL string) {
|
|
ssoError := []byte("Login failed. Please contact your system administrator")
|
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len(ssoError)))
|
|
base64.StdEncoding.Encode(dst, ssoError)
|
|
|
|
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectURL, string(dst)), http.StatusSeeOther)
|
|
}
|
|
|
|
// receiveGoogleAuth completes google OAuth response and forwards a request
|
|
// to front-end to sign user in
|
|
func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) {
|
|
redirectUri := constants.GetDefaultSiteURL()
|
|
ctx := context.Background()
|
|
|
|
if !ah.CheckFeature(model.SSO) {
|
|
zap.L().Error("[receiveGoogleAuth] sso requested but feature unavailable in org domain")
|
|
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
|
|
q := r.URL.Query()
|
|
if errType := q.Get("error"); errType != "" {
|
|
zap.L().Error("[receiveGoogleAuth] failed to login with google auth", zap.String("error", errType), zap.String("error_description", q.Get("error_description")))
|
|
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "failed to login through SSO "), http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
|
|
relayState := q.Get("state")
|
|
zap.L().Debug("[receiveGoogleAuth] relay state received", zap.String("state", relayState))
|
|
|
|
parsedState, err := url.Parse(relayState)
|
|
if err != nil || relayState == "" {
|
|
zap.L().Error("[receiveGoogleAuth] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
// upgrade redirect url from the relay state for better accuracy
|
|
redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login")
|
|
|
|
// fetch domain by parsing relay state.
|
|
domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState)
|
|
if err != nil {
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
// now that we have domain, use domain to fetch sso settings.
|
|
// prepare google callback handler using parsedState -
|
|
// which contains redirect URL (front-end endpoint)
|
|
callbackHandler, err := domain.PrepareGoogleOAuthProvider(parsedState)
|
|
if err != nil {
|
|
zap.L().Error("[receiveGoogleAuth] failed to prepare google oauth provider", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
identity, err := callbackHandler.HandleCallback(r)
|
|
if err != nil {
|
|
zap.L().Error("[receiveGoogleAuth] failed to process HandleCallback ", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
nextPage, err := ah.Signoz.Modules.User.PrepareSsoRedirect(ctx, redirectUri, identity.Email, ah.opts.JWT)
|
|
if err != nil {
|
|
zap.L().Error("[receiveGoogleAuth] failed to generate redirect URI after successful login ", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, nextPage, http.StatusSeeOther)
|
|
}
|
|
|
|
// receiveSAML completes a SAML request and gets user logged in
|
|
func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) {
|
|
// this is the source url that initiated the login request
|
|
redirectUri := constants.GetDefaultSiteURL()
|
|
ctx := context.Background()
|
|
|
|
if !ah.CheckFeature(model.SSO) {
|
|
zap.L().Error("[receiveSAML] sso requested but feature unavailable in org domain")
|
|
http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
zap.L().Error("[receiveSAML] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
// the relay state is sent when a login request is submitted to
|
|
// Idp.
|
|
relayState := r.FormValue("RelayState")
|
|
zap.L().Debug("[receiveML] relay state", zap.String("relayState", relayState))
|
|
|
|
parsedState, err := url.Parse(relayState)
|
|
if err != nil || relayState == "" {
|
|
zap.L().Error("[receiveSAML] failed to process response - invalid response from IDP", zap.Error(err), zap.Any("request", r))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
// upgrade redirect url from the relay state for better accuracy
|
|
redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login")
|
|
|
|
// fetch domain by parsing relay state.
|
|
domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState)
|
|
if err != nil {
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
sp, err := domain.PrepareSamlRequest(parsedState)
|
|
if err != nil {
|
|
zap.L().Error("[receiveSAML] failed to prepare saml request for domain", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
assertionInfo, err := sp.RetrieveAssertionInfo(r.FormValue("SAMLResponse"))
|
|
if err != nil {
|
|
zap.L().Error("[receiveSAML] failed to retrieve assertion info from saml response", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
if assertionInfo.WarningInfo.InvalidTime {
|
|
zap.L().Error("[receiveSAML] expired saml response", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
email := assertionInfo.NameID
|
|
if email == "" {
|
|
zap.L().Error("[receiveSAML] invalid email in the SSO response", zap.String("domain", domain.String()))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
nextPage, err := ah.Signoz.Modules.User.PrepareSsoRedirect(ctx, redirectUri, email, ah.opts.JWT)
|
|
if err != nil {
|
|
zap.L().Error("[receiveSAML] failed to generate redirect URI after successful login ", zap.String("domain", domain.String()), zap.Error(err))
|
|
handleSsoError(w, r, redirectUri)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, nextPage, http.StatusSeeOther)
|
|
}
|