mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-05-23 08:19:42 +02:00
c035e2b117
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
130 lines
2.8 KiB
Go
130 lines
2.8 KiB
Go
package controller
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"encoding/hex"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/phishingclub/phishingclub/data"
|
|
"github.com/phishingclub/phishingclub/errs"
|
|
"github.com/phishingclub/phishingclub/model"
|
|
"github.com/phishingclub/phishingclub/service"
|
|
)
|
|
|
|
const ssoStateCookieKey = "sso_state"
|
|
|
|
// SSO the single sign on controller
|
|
type SSO struct {
|
|
Common
|
|
*service.SSO
|
|
}
|
|
|
|
// Upsert upserts a SSO configuration
|
|
func (s *SSO) Upsert(g *gin.Context) {
|
|
session, _, ok := s.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// parse request
|
|
var request model.SSOOption
|
|
if ok := s.handleParseRequest(g, &request); !ok {
|
|
return
|
|
}
|
|
// handle upsert
|
|
err := s.SSO.Upsert(
|
|
g.Request.Context(),
|
|
session,
|
|
&request,
|
|
)
|
|
// handle responses
|
|
if ok := s.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
s.Response.OK(g, gin.H{})
|
|
}
|
|
|
|
func (s *SSO) IsEnabled(g *gin.Context) {
|
|
// if no sso client is setup, then it is not enabled
|
|
if s.SSO.MSALClient == nil {
|
|
s.Response.OK(g, false)
|
|
return
|
|
}
|
|
s.Response.OK(g, true)
|
|
}
|
|
|
|
func (s *SSO) EntreIDLogin(g *gin.Context) {
|
|
authURL, err := s.SSO.EntreIDLogin(g)
|
|
if errors.Is(err, errs.ErrSSODisabled) {
|
|
s.Response.BadRequest(g)
|
|
return
|
|
}
|
|
if ok := s.handleErrors(g, err); !ok {
|
|
s.Response.BadRequest(g)
|
|
return
|
|
}
|
|
|
|
stateBytes := make([]byte, 32)
|
|
if _, err := rand.Read(stateBytes); err != nil {
|
|
s.Response.ServerError(g)
|
|
return
|
|
}
|
|
state := hex.EncodeToString(stateBytes)
|
|
|
|
http.SetCookie(g.Writer, &http.Cookie{
|
|
Name: ssoStateCookieKey,
|
|
Value: state,
|
|
Path: "/",
|
|
MaxAge: int(5 * time.Minute / time.Second),
|
|
HttpOnly: true,
|
|
Secure: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
|
|
g.Redirect(http.StatusTemporaryRedirect, authURL+"&state="+state)
|
|
}
|
|
|
|
func (s *SSO) EntreIDCallBack(g *gin.Context) {
|
|
stateCookie, err := g.Request.Cookie(ssoStateCookieKey)
|
|
http.SetCookie(g.Writer, &http.Cookie{
|
|
Name: ssoStateCookieKey,
|
|
Value: "",
|
|
Path: "/",
|
|
MaxAge: -1,
|
|
HttpOnly: true,
|
|
Secure: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
stateParam := g.Query("state")
|
|
if err != nil || stateCookie.Value == "" || stateParam == "" ||
|
|
subtle.ConstantTimeCompare([]byte(stateCookie.Value), []byte(stateParam)) != 1 {
|
|
g.Redirect(http.StatusTemporaryRedirect, "/login?ssoAuthError=1")
|
|
return
|
|
}
|
|
|
|
code := g.Query("code")
|
|
session, err := s.SSO.HandlEntraIDCallback(g, code)
|
|
if err != nil {
|
|
g.Redirect(http.StatusTemporaryRedirect, "/login?ssoAuthError=1")
|
|
return
|
|
}
|
|
if ok := s.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
// Set the session in the cookie
|
|
cookie := &http.Cookie{
|
|
Name: data.SessionCookieKey,
|
|
Value: session.ID.String(),
|
|
Path: "/",
|
|
SameSite: http.SameSiteStrictMode,
|
|
HttpOnly: true,
|
|
Secure: true,
|
|
Expires: *session.MaxAgeAt,
|
|
}
|
|
http.SetCookie(g.Writer, cookie)
|
|
g.Redirect(http.StatusTemporaryRedirect, "/dashboard")
|
|
}
|