package controllers import ( "encoding/base64" "encoding/json" "fmt" "net/http" "oc-auth/infrastructure" auth_connectors "oc-auth/infrastructure/auth_connector" "oc-auth/infrastructure/claims" "strings" oclib "cloud.o-forge.io/core/oc-lib" model "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/static" beego "github.com/beego/beego/v2/server/web" ) // Operations about auth type OAuthController struct { beego.Controller } // @Title Logout // @Description unauthenticate user // @Param Authorization header string false "auth token" // @Success 200 {string} // @router /ldap/logout [delete] func (o *OAuthController) LogOutLDAP() { // authorize user reqToken := o.Ctx.Request.Header.Get("Authorization") splitToken := strings.Split(reqToken, "Bearer ") if len(splitToken) < 2 { reqToken = "" } else { reqToken = splitToken[1] } var res auth_connectors.Token json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res) token, err := infrastructure.GetAuthConnector().Logout(reqToken) if err != nil || token == nil { o.Data["json"] = err } else { o.Data["json"] = token } o.ServeJSON() } // @Title Login // @Description authenticate user // @Param body body models.workflow true "The workflow content" // @Success 200 {string} // @router /ldap/login [post] func (o *OAuthController) LoginLDAP() { // authorize user var res auth_connectors.Token json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res) ldap := auth_connectors.New() found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password) if err != nil || !found { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) o.ServeJSON() return } token, err := infrastructure.GetAuthConnector().Login(res.Username, &http.Cookie{ // open a session Name: "csrf_token", Value: o.XSRFToken(), }) if err != nil || token == nil { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) } else { o.Data["json"] = token } o.ServeJSON() } // @Title Claims // @Description enrich token with claims // @Param body body models.Token true "The token info" // @Success 200 {string} // @router /claims [post] func (o *OAuthController) Claims() { // enrich token with claims var res claims.Claims json.Unmarshal(o.Ctx.Input.CopyBody(100000), &res) claims := res.Session.IDToken["id_token_claims"].(map[string]interface{}) userName := claims["sub"].(string) _, loc := static.GetMyLocalJsonPeer() o.Data["json"] = infrastructure.GetClaims().AddClaimsToToken(userName, loc["url"].(string)) o.ServeJSON() } // @Title Introspection // @Description introspect token // @Param body body models.Token true "The token info" // @Success 200 {string} // @router /refresh [post] func (o *OAuthController) Refresh() { var token auth_connectors.Token json.Unmarshal(o.Ctx.Input.CopyBody(100000), &token) // refresh token newToken, err := infrastructure.GetAuthConnector().Refresh(&token) if err != nil || newToken == nil { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) } else { o.Data["json"] = newToken } o.ServeJSON() } // @Title Introspection // @Description introspect token // @Param Authorization header string false "auth token" // @Success 200 {string} // @router /introspect [get] func (o *OAuthController) Introspect() { reqToken := o.Ctx.Request.Header.Get("Authorization") splitToken := strings.Split(reqToken, "Bearer ") if len(splitToken) < 2 { reqToken = "" } else { reqToken = splitToken[1] } token, err := infrastructure.GetAuthConnector().Introspect(reqToken) if err != nil || !token { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) } o.ServeJSON() } // @Title AuthForward // @Description auth forward // @Param Authorization header string false "auth token" // @Param body body models.workflow true "The workflow content" // @Success 200 {string} // @router /forward [get] func (o *OAuthController) InternalAuthForward() { reqToken := o.Ctx.Request.Header.Get("Authorization") splitToken := strings.Split(reqToken, "Bearer ") if len(splitToken) < 2 { reqToken = "" } else { reqToken = splitToken[1] } origin, publicKey, external := o.extractOrigin() if reqToken != "" && !o.checkAuthForward(reqToken, publicKey) && origin != "" { fmt.Println("Unauthorized", origin, reqToken) o.Ctx.ResponseWriter.WriteHeader(401) o.ServeJSON() return } token, err := infrastructure.GetAuthConnector().Introspect(reqToken, &http.Cookie{ Name: "csrf_token", Value: o.XSRFToken(), }) // may be a problem... we should check if token is valid on our side // prefers a refresh token call if err != nil || external { fmt.Println("Unauthorized 2", err, external) // error o.Ctx.ResponseWriter.WriteHeader(401) } else if token && !external { // redirect to login o.Data["json"] = token } o.ServeJSON() } func (o *OAuthController) extractOrigin() (string, string, bool) { external := true publicKey := "" origin := o.Ctx.Request.Header.Get("X-Forwarded-Host") if origin == "" { origin = o.Ctx.Request.Header.Get("Origin") } idLoc, loc := static.GetMyLocalJsonPeer() if origin != "" { // is external peer := oclib.Search(nil, origin, oclib.LibDataEnum(oclib.PEER)) if peer.Code != 200 { return "", "", external } p := peer.Data[0] if strings.Contains(origin, "localhost") || strings.Contains(origin, "127.0.0.1") || idLoc == p.GetID() { external = false } publicKey = p.(*model.Peer).PublicKey } else { external = false publicKey = loc["public_key"].(string) } return origin, publicKey, external } func (o *OAuthController) checkAuthForward(reqToken string, publicKey string) bool { bytes, err := base64.StdEncoding.DecodeString(reqToken) // Converting data if err != nil { fmt.Println("Failed to Decode secret", err) return false } var decodedToken map[string]interface{} err = json.Unmarshal(bytes, &decodedToken) if err != nil { fmt.Println("Failed to parse secret", err) return false } else if decodedToken["session"] != nil { host := o.Ctx.Request.Header.Get("X-Forwarded-Host") method := o.Ctx.Request.Header.Get("X-Forwarded-Method") forward := o.Ctx.Request.Header.Get("X-Forwarded-Uri") if forward == "" || method == "" { fmt.Println("Forwarded headers are missing") return false } // ask keto for permission is in claims ok, err := infrastructure.GetClaims().DecodeClaimsInToken( host, method, forward, decodedToken["session"].(map[string]interface{}), publicKey) if err != nil { fmt.Println("Failed to decode claims", err) } return ok } return false }