Compare commits

..

11 Commits

26 changed files with 407 additions and 1443 deletions

View File

@ -1,32 +1,48 @@
FROM golang:alpine as builder FROM golang:alpine AS deps
WORKDIR /app
COPY go.mod go.sum ./
RUN sed -i '/replace/d' go.mod
RUN cat go.mod
RUN go mod download
#----------------------------------------------------------------------------------------------
FROM golang:alpine AS builder
ARG HOSTNAME=http://localhost ARG HOSTNAME=http://localhost
ARG NAME=local ARG NAME=local
WORKDIR /app RUN apk add git
RUN go install github.com/beego/bee/v2@latest
WORKDIR /oc-auth
COPY --from=deps /go/pkg /go/pkg
COPY --from=deps /app/go.mod /app/go.sum ./
RUN export CGO_ENABLED=0 && \
export GOOS=linux && \
export GOARCH=amd64 && \
export BUILD_FLAGS="-ldflags='-w -s'"
COPY . . COPY . .
RUN apk add git RUN sed -i '/replace/d' go.mod
RUN bee pack
RUN mkdir -p /app/extracted && tar -zxvf oc-auth.tar.gz -C /app/extracted
RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' /app/extracted/swagger/index.html
RUN go get github.com/beego/bee/v2 && go install github.com/beego/bee/v2@master #----------------------------------------------------------------------------------------------
RUN timeout 15 bee run -gendoc=true -downdoc=true -runmode=dev || : FROM golang:alpine
RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' swagger/index.html
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" .
RUN ls /app
FROM scratch
WORKDIR /app WORKDIR /app
COPY --from=builder /app/extracted/oc-auth /usr/bin
COPY --from=builder /app/oc-auth /usr/bin/ COPY --from=builder /app/extracted/swagger /app/swagger
COPY --from=builder /app/swagger /app/swagger COPY --from=builder /app/extracted/pem /app/pem
COPY --from=builder /app/extracted/docker_auth.json /etc/oc/auth.json
COPY docker_auth.json /etc/oc/auth.json
EXPOSE 8080 EXPOSE 8080

27
Makefile Normal file
View File

@ -0,0 +1,27 @@
.DEFAULT_GOAL := all
build: clean
bee pack
run:
bee run -gendoc=true -downdoc=true
debug:
bee run -downdebug -gendebug
clean:
rm -rf oc-auth oc-auth.tar.gz
docker:
DOCKER_BUILDKIT=1 docker build -t oc/oc-auth:0.0.1 -f Dockerfile .
docker tag oc/oc-auth:0.0.1 oc/oc-auth:latest
publish-kind:
kind load docker-image oc/oc-auth:0.0.1 --name opencloud
publish-registry:
@echo "TODO"
all: docker publish-kind publish-registry
.PHONY: build run clean docker publish-kind publish-registry

Binary file not shown.

View File

@ -3,7 +3,6 @@ package conf
import "sync" import "sync"
type Config struct { type Config struct {
SourceMode string
AdminRole string AdminRole string
PublicKeyPath string PublicKeyPath string
PrivateKeyPath string PrivateKeyPath string
@ -14,9 +13,12 @@ type Config struct {
LDAPBaseDN string LDAPBaseDN string
LDAPRoleBaseDN string LDAPRoleBaseDN string
ClientSecret string ClientSecret string
OAuth2ClientSecretName string
OAuth2ClientSecretNamespace string
Auth string Auth string
AuthConnectPublicHost string
AuthConnectorHost string AuthConnectorHost string
AuthConnectorPort int AuthConnectorPort int
AuthConnectorAdminPort int AuthConnectorAdminPort int

View File

@ -19,8 +19,7 @@ type GroupController struct {
func (o *GroupController) Post() { func (o *GroupController) Post() {
// store and return Id or post with UUID // store and return Id or post with UUID
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) group, code, err := infrastructure.GetPermissionConnector().CreateGroup(id)
group, code, err := infrastructure.GetPermissionConnector(clientID).CreateGroup(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -45,8 +44,7 @@ func (o *GroupController) Post() {
// @router /user/:id [get] // @router /user/:id [get]
func (o *GroupController) GetByUser() { func (o *GroupController) GetByUser() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) group, err := infrastructure.GetPermissionConnector().GetGroupByUser(id)
group, err := infrastructure.GetPermissionConnector(clientID).GetGroupByUser(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -69,8 +67,7 @@ func (o *GroupController) GetByUser() {
// @Success 200 {group} string // @Success 200 {group} string
// @router / [get] // @router / [get]
func (o *GroupController) GetAll() { func (o *GroupController) GetAll() {
clientID := ExtractClient(*o.Ctx.Request) group, err := infrastructure.GetPermissionConnector().GetGroup("")
group, err := infrastructure.GetPermissionConnector(clientID).GetGroup("")
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -95,8 +92,7 @@ func (o *GroupController) GetAll() {
// @router /:id [get] // @router /:id [get]
func (o *GroupController) Get() { func (o *GroupController) Get() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) group, err := infrastructure.GetPermissionConnector().GetGroup(id)
group, err := infrastructure.GetPermissionConnector(clientID).GetGroup(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -121,8 +117,7 @@ func (o *GroupController) Get() {
// @router /:id [delete] // @router /:id [delete]
func (o *GroupController) Delete() { func (o *GroupController) Delete() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) group, code, err := infrastructure.GetPermissionConnector().DeleteGroup(id)
group, code, err := infrastructure.GetPermissionConnector(clientID).DeleteGroup(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -145,8 +140,7 @@ func (o *GroupController) Delete() {
// @Success 200 {string} delete success! // @Success 200 {string} delete success!
// @router /clear [delete] // @router /clear [delete]
func (o *GroupController) Clear() { func (o *GroupController) Clear() {
clientID := ExtractClient(*o.Ctx.Request) group, code, err := infrastructure.GetPermissionConnector().DeleteGroup("")
group, code, err := infrastructure.GetPermissionConnector(clientID).DeleteGroup("")
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -173,8 +167,7 @@ func (o *GroupController) Clear() {
func (o *GroupController) Bind() { func (o *GroupController) Bind() {
user_id := o.Ctx.Input.Param(":user_id") user_id := o.Ctx.Input.Param(":user_id")
group_id := o.Ctx.Input.Param(":group_id") group_id := o.Ctx.Input.Param(":group_id")
clientID := ExtractClient(*o.Ctx.Request) group, code, err := infrastructure.GetPermissionConnector().BindGroup(user_id, group_id)
group, code, err := infrastructure.GetPermissionConnector(clientID).BindGroup(user_id, group_id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -194,15 +187,14 @@ func (o *GroupController) Bind() {
// @Title UnBind // @Title UnBind
// @Description unbind the group to user // @Description unbind the group to user
// @Param user_id path string true "The group_id you want to unbind" // @Param group_id path string true "The group_id you want to unbind"
// @Param group_id path string true "The user_id you want to unbind" // @Param group_id path string true "The user_id you want to unbind"
// @Success 200 {string} bind success! // @Success 200 {string} bind success!
// @router /:user_id/:group_id [delete] // @router /:user_id/:group_id [delete]
func (o *GroupController) UnBind() { func (o *GroupController) UnBind() {
user_id := o.Ctx.Input.Param(":user_id") user_id := o.Ctx.Input.Param(":user_id")
group_id := o.Ctx.Input.Param(":group_id") group_id := o.Ctx.Input.Param(":group_id")
clientID := ExtractClient(*o.Ctx.Request) group, code, err := infrastructure.GetPermissionConnector().UnBindGroup(user_id, group_id)
group, code, err := infrastructure.GetPermissionConnector(clientID).UnBindGroup(user_id, group_id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,

View File

@ -1,11 +1,9 @@
package controllers package controllers
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"oc-auth/conf"
"oc-auth/infrastructure" "oc-auth/infrastructure"
auth_connectors "oc-auth/infrastructure/auth_connector" auth_connectors "oc-auth/infrastructure/auth_connector"
"regexp" "regexp"
@ -24,12 +22,10 @@ type OAuthController struct {
// @Title Logout // @Title Logout
// @Description unauthenticate user // @Description unauthenticate user
// @Param Authorization header string false "auth token" // @Param Authorization header string false "auth token"
// @Param client_id query string true "the client_id you want to get"
// @Success 200 {string} // @Success 200 {string}
// @router /logout [delete] // @router /ldap/logout [delete]
func (o *OAuthController) LogOut() { func (o *OAuthController) LogOutLDAP() {
// authorize user // authorize user
clientID := o.Ctx.Input.Query("client_id")
reqToken := o.Ctx.Request.Header.Get("Authorization") reqToken := o.Ctx.Request.Header.Get("Authorization")
splitToken := strings.Split(reqToken, "Bearer ") splitToken := strings.Split(reqToken, "Bearer ")
if len(splitToken) < 2 { if len(splitToken) < 2 {
@ -40,7 +36,7 @@ func (o *OAuthController) LogOut() {
var res auth_connectors.Token var res auth_connectors.Token
json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res) json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res)
token, err := infrastructure.GetAuthConnector().Logout(clientID, reqToken) token, err := infrastructure.GetAuthConnector().Logout(reqToken)
if err != nil || token == nil { if err != nil || token == nil {
o.Data["json"] = err o.Data["json"] = err
} else { } else {
@ -52,33 +48,25 @@ func (o *OAuthController) LogOut() {
// @Title Login // @Title Login
// @Description authenticate user // @Description authenticate user
// @Param body body models.workflow true "The workflow content" // @Param body body models.workflow true "The workflow content"
// @Param client_id query string true "the client_id you want to get"
// @Success 200 {string} // @Success 200 {string}
// @router /login [post] // @router /ldap/login [post]
func (o *OAuthController) Login() { func (o *OAuthController) LoginLDAP() {
// authorize user // authorize user
fmt.Println("Login", o.Ctx.Input.Query("client_id"), o.Ctx.Input.Param(":client_id"))
clientID := o.Ctx.Input.Query("client_id")
var res auth_connectors.Token var res auth_connectors.Token
json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res) json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res)
if conf.GetConfig().SourceMode == "ldap" { ldap := auth_connectors.New()
ldap := auth_connectors.New() found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password)
found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password) if err != nil || !found {
fmt.Println("found", found, "err", err) o.Data["json"] = err
if err != nil || !found { o.Ctx.ResponseWriter.WriteHeader(401)
o.Data["json"] = err o.ServeJSON()
o.Ctx.ResponseWriter.WriteHeader(401) return
o.ServeJSON()
return
}
} }
token, err := infrastructure.GetAuthConnector().Login( token, err := infrastructure.GetAuthConnector().Login(res.Username,
clientID, res.Username,
&http.Cookie{ // open a session &http.Cookie{ // open a session
Name: "csrf_token", Name: "csrf_token",
Value: o.XSRFToken(), Value: o.XSRFToken(),
}) })
fmt.Println("token", token, "err", err)
if err != nil || token == nil { if err != nil || token == nil {
o.Data["json"] = err o.Data["json"] = err
o.Ctx.ResponseWriter.WriteHeader(401) o.Ctx.ResponseWriter.WriteHeader(401)
@ -91,15 +79,13 @@ func (o *OAuthController) Login() {
// @Title Introspection // @Title Introspection
// @Description introspect token // @Description introspect token
// @Param body body models.Token true "The token info" // @Param body body models.Token true "The token info"
// @Param client_id query string true "the client_id you want to get"
// @Success 200 {string} // @Success 200 {string}
// @router /refresh [post] // @router /refresh [post]
func (o *OAuthController) Refresh() { func (o *OAuthController) Refresh() {
clientID := o.Ctx.Input.Query("client_id")
var token auth_connectors.Token var token auth_connectors.Token
json.Unmarshal(o.Ctx.Input.CopyBody(100000), &token) json.Unmarshal(o.Ctx.Input.CopyBody(100000), &token)
// refresh token // refresh token
newToken, err := infrastructure.GetAuthConnector().Refresh(clientID, &token) newToken, err := infrastructure.GetAuthConnector().Refresh(&token)
if err != nil || newToken == nil { if err != nil || newToken == nil {
o.Data["json"] = err o.Data["json"] = err
o.Ctx.ResponseWriter.WriteHeader(401) o.Ctx.ResponseWriter.WriteHeader(401)
@ -142,7 +128,7 @@ var whitelist = []string{
// @Param Authorization header string false "auth token" // @Param Authorization header string false "auth token"
// @Success 200 {string} // @Success 200 {string}
// @router /forward [get] // @router /forward [get]
func (o *OAuthController) InternaisDraftlAuthForward() { func (o *OAuthController) InternalAuthForward() {
fmt.Println("InternalAuthForward") fmt.Println("InternalAuthForward")
reqToken := o.Ctx.Request.Header.Get("Authorization") reqToken := o.Ctx.Request.Header.Get("Authorization")
if reqToken == "" { if reqToken == "" {
@ -163,7 +149,7 @@ func (o *OAuthController) InternaisDraftlAuthForward() {
} else { } else {
reqToken = splitToken[1] reqToken = splitToken[1]
} }
origin, publicKey, external := o.extractOrigin(o.Ctx.Request) origin, publicKey, external := o.extractOrigin()
if !infrastructure.GetAuthConnector().CheckAuthForward( //reqToken != "" && if !infrastructure.GetAuthConnector().CheckAuthForward( //reqToken != "" &&
reqToken, publicKey, origin, reqToken, publicKey, origin,
o.Ctx.Request.Header.Get("X-Forwarded-Method"), o.Ctx.Request.Header.Get("X-Forwarded-Method"),
@ -175,8 +161,7 @@ func (o *OAuthController) InternaisDraftlAuthForward() {
o.ServeJSON() o.ServeJSON()
} }
func (o *OAuthController) extractOrigin(request *http.Request) (string, string, bool) { func (o *OAuthController) extractOrigin() (string, string, bool) {
user, peerID, groups := oclib.ExtractTokenInfo(*request)
external := true external := true
publicKey := "" publicKey := ""
origin := o.Ctx.Request.Header.Get("X-Forwarded-Host") origin := o.Ctx.Request.Header.Get("X-Forwarded-Host")
@ -189,7 +174,7 @@ func (o *OAuthController) extractOrigin(request *http.Request) (string, string,
if t != "" { if t != "" {
searchStr = strings.Replace(searchStr, t, "", -1) searchStr = strings.Replace(searchStr, t, "", -1)
} }
peer := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), user, peerID, groups, nil).Search(nil, searchStr, false) peer := oclib.Search(nil, searchStr, oclib.LibDataEnum(oclib.PEER))
if peer.Code != 200 || len(peer.Data) == 0 { // TODO: add state of partnership if peer.Code != 200 || len(peer.Data) == 0 { // TODO: add state of partnership
return "", "", external return "", "", external
} }
@ -205,29 +190,3 @@ func (o *OAuthController) extractOrigin(request *http.Request) (string, string,
} }
return origin, publicKey, external return origin, publicKey, external
} }
func ExtractClient(request http.Request) string {
reqToken := request.Header.Get("Authorization")
splitToken := strings.Split(reqToken, "Bearer ")
if len(splitToken) < 2 {
reqToken = ""
} else {
reqToken = splitToken[1]
}
if reqToken != "" {
token := strings.Split(reqToken, ".")
if len(token) > 2 {
bytes, err := base64.StdEncoding.DecodeString(token[2])
if err != nil {
return ""
}
m := map[string]interface{}{}
err = json.Unmarshal(bytes, &m)
if err != nil {
return ""
}
return m["session"].(map[string]interface{})["id_token"].(map[string]interface{})["client_id"].(string)
}
}
return ""
}

View File

@ -16,8 +16,7 @@ type PermissionController struct {
// @Success 200 {permission} string // @Success 200 {permission} string
// @router / [get] // @router / [get]
func (o *PermissionController) GetAll() { func (o *PermissionController) GetAll() {
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetPermission("", "")
role, err := infrastructure.GetPermissionConnector(clientID).GetPermission("", "")
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -42,8 +41,7 @@ func (o *PermissionController) GetAll() {
// @router /role/:id [get] // @router /role/:id [get]
func (o *PermissionController) GetByRole() { func (o *PermissionController) GetByRole() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetPermissionByRole(id)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermissionByRole(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -68,8 +66,7 @@ func (o *PermissionController) GetByRole() {
// @router /user/:id [get] // @router /user/:id [get]
func (o *PermissionController) GetByUser() { func (o *PermissionController) GetByUser() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetPermissionByUser(id, true)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermissionByUser(id, true)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -95,8 +92,7 @@ func (o *PermissionController) GetByUser() {
func (o *PermissionController) Get() { func (o *PermissionController) Get() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
rel := o.Ctx.Input.Param(":relation") rel := o.Ctx.Input.Param(":relation")
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetPermission(id, rel)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermission(id, rel)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -119,8 +115,7 @@ func (o *PermissionController) Get() {
// @Success 200 {string} delete success! // @Success 200 {string} delete success!
// @router /clear [delete] // @router /clear [delete]
func (o *PermissionController) Clear() { func (o *PermissionController) Clear() {
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().DeletePermission("", "", true)
role, code, err := infrastructure.GetPermissionConnector(clientID).DeletePermission("", "", true)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -149,8 +144,7 @@ func (o *PermissionController) Bind() {
permission_id := o.Ctx.Input.Param(":permission_id") permission_id := o.Ctx.Input.Param(":permission_id")
role_id := o.Ctx.Input.Param(":role_id") role_id := o.Ctx.Input.Param(":role_id")
rel := o.Ctx.Input.Param(":relation") rel := o.Ctx.Input.Param(":relation")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().BindPermission(role_id, permission_id, rel)
role, code, err := infrastructure.GetPermissionConnector(clientID).BindPermission(role_id, permission_id, rel)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -179,8 +173,7 @@ func (o *PermissionController) UnBind() {
permission_id := o.Ctx.Input.Param(":permission_id") permission_id := o.Ctx.Input.Param(":permission_id")
role_id := o.Ctx.Input.Param(":role_id") role_id := o.Ctx.Input.Param(":role_id")
rel := o.Ctx.Input.Param(":relation") rel := o.Ctx.Input.Param(":relation")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().UnBindPermission(role_id, permission_id, rel)
role, code, err := infrastructure.GetPermissionConnector(clientID).UnBindPermission(role_id, permission_id, rel)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,

View File

@ -19,8 +19,7 @@ type RoleController struct {
func (o *RoleController) Post() { func (o *RoleController) Post() {
// store and return Id or post with UUID // store and return Id or post with UUID
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().CreateRole(id)
role, code, err := infrastructure.GetPermissionConnector(clientID).CreateRole(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -45,8 +44,7 @@ func (o *RoleController) Post() {
// @router /user/:id [get] // @router /user/:id [get]
func (o *RoleController) GetByUser() { func (o *RoleController) GetByUser() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetRoleByUser(id)
role, err := infrastructure.GetPermissionConnector(clientID).GetRoleByUser(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -69,8 +67,7 @@ func (o *RoleController) GetByUser() {
// @Success 200 {role} string // @Success 200 {role} string
// @router / [get] // @router / [get]
func (o *RoleController) GetAll() { func (o *RoleController) GetAll() {
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetRole("")
role, err := infrastructure.GetPermissionConnector(clientID).GetRole("")
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -95,8 +92,7 @@ func (o *RoleController) GetAll() {
// @router /:id [get] // @router /:id [get]
func (o *RoleController) Get() { func (o *RoleController) Get() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, err := infrastructure.GetPermissionConnector().GetRole(id)
role, err := infrastructure.GetPermissionConnector(clientID).GetRole(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -121,8 +117,7 @@ func (o *RoleController) Get() {
// @router /:id [delete] // @router /:id [delete]
func (o *RoleController) Delete() { func (o *RoleController) Delete() {
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().DeleteRole(id)
role, code, err := infrastructure.GetPermissionConnector(clientID).DeleteRole(id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -145,8 +140,7 @@ func (o *RoleController) Delete() {
// @Success 200 {string} delete success! // @Success 200 {string} delete success!
// @router /clear [delete] // @router /clear [delete]
func (o *RoleController) Clear() { func (o *RoleController) Clear() {
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().DeleteRole("")
role, code, err := infrastructure.GetPermissionConnector(clientID).DeleteRole("")
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -173,8 +167,7 @@ func (o *RoleController) Clear() {
func (o *RoleController) Bind() { func (o *RoleController) Bind() {
user_id := o.Ctx.Input.Param(":user_id") user_id := o.Ctx.Input.Param(":user_id")
role_id := o.Ctx.Input.Param(":role_id") role_id := o.Ctx.Input.Param(":role_id")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().BindRole(user_id, role_id)
role, code, err := infrastructure.GetPermissionConnector(clientID).BindRole(user_id, role_id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,
@ -201,8 +194,7 @@ func (o *RoleController) Bind() {
func (o *RoleController) UnBind() { func (o *RoleController) UnBind() {
user_id := o.Ctx.Input.Param(":user_id") user_id := o.Ctx.Input.Param(":user_id")
role_id := o.Ctx.Input.Param(":role_id") role_id := o.Ctx.Input.Param(":role_id")
clientID := ExtractClient(*o.Ctx.Request) role, code, err := infrastructure.GetPermissionConnector().UnBindRole(user_id, role_id)
role, code, err := infrastructure.GetPermissionConnector(clientID).UnBindRole(user_id, role_id)
if err != nil { if err != nil {
o.Data["json"] = map[string]interface{}{ o.Data["json"] = map[string]interface{}{
"data": nil, "data": nil,

View File

@ -1,21 +0,0 @@
version: '3.4'
services:
oc-auth-2:
image: 'oc-auth-2:latest'
ports:
- 8095:8080
container_name: oc-auth-2
environment:
LDAP_ENDPOINTS: ldap-2:389
LDAP_BINDDN: cn=admin,dc=example,dc=com
LDAP_BINDPW: password
LDAP_BASEDN: "dc=example,dc=com"
LDAP_ROLE_BASEDN: "ou=AppRoles,dc=example,dc=com"
networks:
- catalog
volumes:
- ./pem:/etc/oc/pem
networks:
catalog:
external: true

112
go.mod
View File

@ -1,85 +1,59 @@
module oc-auth module oc-auth
go 1.22.0 go 1.23.0
toolchain go1.23.3
require ( require (
cloud.o-forge.io/core/oc-lib v0.0.0-20250117152246-b85ca8674b27 cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be
github.com/beego/beego/v2 v2.3.1 github.com/beego/beego/v2 v2.3.1
github.com/nats-io/nats.go v1.37.0
github.com/ory/hydra-client-go v1.11.8
github.com/smartystreets/goconvey v1.7.2 github.com/smartystreets/goconvey v1.7.2
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.23.0 k8s.io/apimachinery v0.32.1
k8s.io/client-go v0.32.1
) )
//replace cloud.o-forge.io/core/oc-lib => ../oc-lib
require ( require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/biter777/countries v1.7.5 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/gobuffalo/pop/v6 v6.0.8 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.0 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/marcinwyszynski/geopoint v0.0.0-20140302213024-cf2a6f750c5b // indirect github.com/nats-io/nats.go v1.37.0 // indirect
github.com/mattn/goveralls v0.0.12 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe // indirect
github.com/ory/go-convenience v0.1.0 // indirect
github.com/ory/x v0.0.575 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/robfig/cron v1.2.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.42.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.11.0 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/term v0.25.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect gopkg.in/inf.v0 v0.9.1 // indirect
google.golang.org/grpc v1.63.0 // indirect k8s.io/api v0.32.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
) )
require ( require (
@ -91,7 +65,6 @@ require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
@ -99,10 +72,7 @@ require (
github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/i-core/rlog v1.0.0 github.com/i-core/rlog v1.0.0
github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/justinas/nosurf v1.1.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
@ -111,13 +81,10 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect github.com/nats-io/nuid v1.0.1 // indirect
github.com/ory/fosite v0.47.0
github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/zerolog v1.33.0 // indirect github.com/rs/zerolog v1.33.0 // indirect
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/assertions v1.2.0 // indirect
@ -131,7 +98,6 @@ require (
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.19.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

955
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,10 @@ import (
type AuthConnector interface { type AuthConnector interface {
Status() tools.State Status() tools.State
Login(clientID string, username string, cookies ...*http.Cookie) (*Token, error) Login(username string, cookies ...*http.Cookie) (*Token, error)
Logout(clientID string, token string, cookies ...*http.Cookie) (*Token, error) Logout(token string, cookies ...*http.Cookie) (*Token, error)
Introspect(token string, cookie ...*http.Cookie) (bool, error) Introspect(token string, cookie ...*http.Cookie) (bool, error)
Refresh(client_id string, token *Token) (*Token, error) Refresh(token *Token) (*Token, error)
CheckAuthForward(reqToken string, publicKey string, host string, method string, forward string, external bool) bool CheckAuthForward(reqToken string, publicKey string, host string, method string, forward string, external bool) bool
} }

View File

@ -1,6 +1,8 @@
package auth_connectors package auth_connectors
import ( import (
"bytes"
"context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
@ -10,6 +12,7 @@ import (
"net/url" "net/url"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure/claims" "oc-auth/infrastructure/claims"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -18,11 +21,16 @@ import (
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
) )
type HydraConnector struct { type HydraConnector struct {
State string `json:"state"` State string `json:"state"`
Scopes string `json:"scope"` Scopes string `json:"scope"`
ClientID string `json:"client_id"`
ResponseType string `json:"response_type"` ResponseType string `json:"response_type"`
Caller *tools.HTTPCaller Caller *tools.HTTPCaller
@ -84,7 +92,7 @@ func (a HydraConnector) challenge(username string, url string, challenge string,
return &token, s[1], cookies, nil return &token, s[1], cookies, nil
} }
func (a HydraConnector) Refresh(client_id string, token *Token) (*Token, error) { func (a HydraConnector) Refresh(token *Token) (*Token, error) {
access := strings.Split(token.AccessToken, ".") access := strings.Split(token.AccessToken, ".")
if len(access) > 2 { if len(access) > 2 {
token.AccessToken = strings.Join(access[0:2], ".") token.AccessToken = strings.Join(access[0:2], ".")
@ -93,20 +101,34 @@ func (a HydraConnector) Refresh(client_id string, token *Token) (*Token, error)
if err != nil || !isValid { if err != nil || !isValid {
return nil, err return nil, err
} }
_, err = a.Logout(client_id, token.AccessToken) _, err = a.Logout(token.AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.Login(client_id, token.Username) return a.Login(token.Username)
} }
func (a HydraConnector) tryLog(username string, url string, subpath string, challenge string, cookies ...*http.Cookie) (*Redirect, string, []*http.Cookie, error) { func (a HydraConnector) tryLog(username string, url string, subpath string, challenge string, cookies ...*http.Cookie) (*Redirect, string, []*http.Cookie, error) {
resp, err := a.Caller.CallRaw(http.MethodGet, url, subpath,
map[string]interface{}{}, "application/json", true, cookies...) postBody, _ := json.Marshal(map[string]interface{}{})
if err != nil || resp.Request.Response == nil || resp.Request.Response.Header["Set-Cookie"] == nil { responseBody := bytes.NewBuffer(postBody)
req, _ := http.NewRequest(http.MethodGet, url+subpath, responseBody)
req.Header.Set("Content-Type", "application/json")
req.Header.Add("X-Forwarded-Proto", "https")
for _, c := range cookies {
req.AddCookie(c)
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // No redirect, doesn't make sense; hydra redirect user to login page, we are not the user here due to wrong oauth flow implementation
},
}
resp, err := client.Do(req)
if err != nil || resp == nil || resp.Header["Set-Cookie"] == nil {
return nil, "", cookies, err return nil, "", cookies, err
} }
cc := resp.Request.Response.Header["Set-Cookie"] // retrieve oauth2 csrf token cookie cc := resp.Header["Set-Cookie"] // retrieve oauth2 csrf token cookie
if len(cc) > 0 { if len(cc) > 0 {
for _, c := range cc { for _, c := range cc {
first := strings.Split(c, ";") first := strings.Split(c, ";")
@ -116,10 +138,10 @@ func (a HydraConnector) tryLog(username string, url string, subpath string, chal
}) })
} }
} }
return a.challenge(username, resp.Request.URL.String(), challenge, cookies...) return a.challenge(username, resp.Header.Get("Location"), challenge, cookies...)
} }
func (a HydraConnector) getClient(clientID string) string { func (a HydraConnector) getClient() string {
resp, err := a.Caller.CallGet(a.getPath(true, false), "/clients") resp, err := a.Caller.CallGet(a.getPath(true, false), "/clients")
if err != nil { if err != nil {
return "" return ""
@ -129,17 +151,11 @@ func (a HydraConnector) getClient(clientID string) string {
if err != nil || len(clients) == 0 { if err != nil || len(clients) == 0 {
return "" return ""
} }
for _, c := range clients {
if c.(map[string]interface{})["client_name"].(string) == clientID {
return c.(map[string]interface{})["client_id"].(string)
}
}
return clients[0].(map[string]interface{})["client_id"].(string) return clients[0].(map[string]interface{})["client_id"].(string)
} }
func (a HydraConnector) Login(clientID string, username string, cookies ...*http.Cookie) (t *Token, err error) { func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Token, err error) {
fmt.Println("login", clientID, username) clientID := a.getClient()
clientID = a.getClient(clientID)
redirect, _, cookies, err := a.tryLog(username, a.getPath(false, true), redirect, _, cookies, err := a.tryLog(username, a.getPath(false, true),
"/auth?client_id="+clientID+"&response_type="+strings.ReplaceAll(a.ResponseType, " ", "%20")+"&scope="+strings.ReplaceAll(a.Scopes, " ", "%20")+"&state="+a.State, "/auth?client_id="+clientID+"&response_type="+strings.ReplaceAll(a.ResponseType, " ", "%20")+"&scope="+strings.ReplaceAll(a.Scopes, " ", "%20")+"&state="+a.State,
"login", cookies...) "login", cookies...)
@ -151,8 +167,22 @@ func (a HydraConnector) Login(clientID string, username string, cookies ...*http
return nil, err return nil, err
} }
// problem with consent THERE we need to accept the consent challenge && get the token // problem with consent THERE we need to accept the consent challenge && get the token
_, err = a.Caller.CallRaw(http.MethodGet, a.urlFormat(redirect.RedirectTo, a.getPath(false, true)), "", map[string]interface{}{},
"application/json", true, cookies...) postBody, _ := json.Marshal(map[string]interface{}{})
responseBody := bytes.NewBuffer(postBody)
req, _ := http.NewRequest(http.MethodGet, a.urlFormat(redirect.RedirectTo, a.getPath(false, true)), responseBody)
req.Header.Set("Content-Type", "application/json")
req.Header.Add("X-Forwarded-Proto", "https")
for _, c := range cookies {
req.AddCookie(c)
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // No redirect, doesn't make sense; hydra redirect user to login page, we are not the user here due to wrong oauth flow implementation
},
}
_, err = client.Do(req)
if err != nil { if err != nil {
s := strings.Split(err.Error(), "\"") s := strings.Split(err.Error(), "\"")
if len(s) > 1 && strings.Contains(s[1], "access_token") { if len(s) > 1 && strings.Contains(s[1], "access_token") {
@ -165,6 +195,15 @@ func (a HydraConnector) Login(clientID string, username string, cookies ...*http
Username: username, Username: username,
} }
urls := url.Values{} urls := url.Values{}
// Using k8s secrets gen by hydra, eventually
clientID, clientSecret, err := a.getOAuth2Conf(conf.GetConfig().OAuth2ClientSecretNamespace, conf.GetConfig().OAuth2ClientSecretName)
if err == nil {
urls.Add("client_id", clientID)
urls.Add("client_secret", clientSecret)
}
// Fallback on manually set client secret
urls.Add("client_id", clientID) urls.Add("client_id", clientID)
urls.Add("client_secret", conf.GetConfig().ClientSecret) urls.Add("client_secret", conf.GetConfig().ClientSecret)
urls.Add("grant_type", "client_credentials") urls.Add("grant_type", "client_credentials")
@ -181,7 +220,7 @@ func (a HydraConnector) Login(clientID string, username string, cookies ...*http
return nil, err return nil, err
} }
json.Unmarshal(b, &m) json.Unmarshal(b, &m)
pp := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), false) pp := oclib.Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), oclib.LibDataEnum(oclib.PEER))
if len(pp.Data) == 0 || pp.Code >= 300 || pp.Err != "" { if len(pp.Data) == 0 || pp.Code >= 300 || pp.Err != "" {
return nil, errors.New("peer not found") return nil, errors.New("peer not found")
} }
@ -189,8 +228,7 @@ func (a HydraConnector) Login(clientID string, username string, cookies ...*http
now = now.Add(time.Duration(token.ExpiresIn) * time.Second) now = now.Add(time.Duration(token.ExpiresIn) * time.Second)
unix := now.Unix() unix := now.Unix()
c := claims.GetClaims().AddClaimsToToken(clientID, username, pp.Data[0].(*peer.Peer)) c := claims.GetClaims().AddClaimsToToken(username, pp.Data[0].(*peer.Peer))
fmt.Println("claims", c.Session.AccessToken)
c.Session.AccessToken["exp"] = unix c.Session.AccessToken["exp"] = unix
b, _ = json.Marshal(c) b, _ = json.Marshal(c)
@ -200,8 +238,55 @@ func (a HydraConnector) Login(clientID string, username string, cookies ...*http
return token, nil return token, nil
} }
func (a HydraConnector) Logout(clientID string, token string, cookies ...*http.Cookie) (*Token, error) { func (a HydraConnector) getOAuth2Conf(namespace string, secretName string) (string, string, error) {
clientID = a.getClient(clientID) clientset, err := a.getClientset()
if err != nil {
return "", "", fmt.Errorf("error creating Kubernetes client: %v", err)
}
secret, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
if err != nil {
return "", "", fmt.Errorf("error retrieving secret %s/%s: %v", namespace, secretName, err)
}
clientIDEncoded, found := secret.Data["CLIENT_ID"]
if !found {
return "", "", fmt.Errorf("CLIENT_ID key not found in secret")
}
clientSecretEncoded, found := secret.Data["CLIENT_SECRET"]
if !found {
return "", "", fmt.Errorf("CLIENT_SECRET key not found in secret")
}
clientID := string(clientIDEncoded)
clientSecret := string(clientSecretEncoded)
return clientID, clientSecret, nil
}
func (a HydraConnector) getClientset() (*kubernetes.Clientset, error) {
var config *rest.Config
var err error
// Check if running inside cluster
if _, inCluster := os.LookupEnv("KUBERNETES_SERVICE_HOST"); inCluster {
config, err = rest.InClusterConfig() // Use in-cluster config
} else {
kubeconfig := os.Getenv("KUBECONFIG") // Use local kubeconfig file
if kubeconfig == "" {
kubeconfig = clientcmd.RecommendedHomeFile
}
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
}
if err != nil {
return nil, err
}
return kubernetes.NewForConfig(config)
}
func (a HydraConnector) Logout(token string, cookies ...*http.Cookie) (*Token, error) {
access := strings.Split(token, ".") access := strings.Split(token, ".")
if len(access) > 2 { if len(access) > 2 {
token = strings.Join(access[0:2], ".") token = strings.Join(access[0:2], ".")
@ -209,7 +294,7 @@ func (a HydraConnector) Logout(clientID string, token string, cookies ...*http.C
p := a.getPath(false, true) + "/revoke" p := a.getPath(false, true) + "/revoke"
urls := url.Values{} urls := url.Values{}
urls.Add("token", token) urls.Add("token", token)
urls.Add("client_id", clientID) urls.Add("client_id", a.getClient())
urls.Add("client_secret", conf.GetConfig().ClientSecret) urls.Add("client_secret", conf.GetConfig().ClientSecret)
_, err := a.Caller.CallForm(http.MethodPost, p, "", urls, "application/x-www-form-urlencoded", true) _, err := a.Caller.CallForm(http.MethodPost, p, "", urls, "application/x-www-form-urlencoded", true)
if err != nil { if err != nil {
@ -249,9 +334,10 @@ func (a HydraConnector) Introspect(token string, cookie ...*http.Cookie) (bool,
} }
func (a HydraConnector) getPath(isAdmin bool, isOauth bool) string { func (a HydraConnector) getPath(isAdmin bool, isOauth bool) string {
host := conf.GetConfig().AuthConnectorHost host := conf.GetConfig().AuthConnectPublicHost
port := fmt.Sprintf("%v", conf.GetConfig().AuthConnectorPort) port := fmt.Sprintf("%v", conf.GetConfig().AuthConnectorPort)
if isAdmin { if isAdmin {
host = conf.GetConfig().AuthConnectorHost
port = fmt.Sprintf("%v", conf.GetConfig().AuthConnectorAdminPort) + "/admin" port = fmt.Sprintf("%v", conf.GetConfig().AuthConnectorAdminPort) + "/admin"
} }
oauth := "" oauth := ""

View File

@ -31,9 +31,8 @@ var (
type conn interface { type conn interface {
Bind(bindDN, password string) error Bind(bindDN, password string) error
SearchRoles(attrs ...string) ([]map[string][]string, error) SearchUser(user string, attrs ...string) ([]map[string]interface{}, error)
SearchUser(user string, attrs ...string) ([]map[string][]string, error) SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error)
SearchUserRoles(user string, attrs ...string) ([]map[string][]string, error)
Close() error Close() error
} }
@ -79,7 +78,7 @@ type Client struct {
cache *freecache.Cache cache *freecache.Cache
} }
func (cli *Client) Authenticate(ctx context.Context, username string, password string) (bool, error) { func (cli *Client) Authenticate(ctx context.Context, username, password string) (bool, error) {
if username == "" || password == "" { if username == "" || password == "" {
return false, nil return false, nil
} }
@ -102,8 +101,8 @@ func (cli *Client) Authenticate(ctx context.Context, username string, password s
if details == nil { if details == nil {
return false, nil return false, nil
} }
a := details["dn"]
if err := cn.Bind(a[0], password); err != nil { if err := cn.Bind(details["dn"].(string), password); err != nil {
if err == errInvalidCredentials { if err == errInvalidCredentials {
return false, nil return false, nil
} }
@ -119,21 +118,6 @@ func (cli *Client) Authenticate(ctx context.Context, username string, password s
return true, nil return true, nil
} }
func (cli *Client) GetRoles(ctx context.Context) (map[string]LDAPRoles, error) {
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
cn, ok := <-cli.connect(ctx)
cancel()
if !ok {
return map[string]LDAPRoles{}, errConnectionTimeout
}
defer cn.Close()
// Find a user DN by his or her username.
return cli.findRoles(cn, "dn", "member", "uniqueMember")
}
// Claim is the FindOIDCClaims result struct // Claim is the FindOIDCClaims result struct
type LDAPClaim struct { type LDAPClaim struct {
Code string // the root claim name Code string // the root claim name
@ -141,10 +125,6 @@ type LDAPClaim struct {
Value interface{} // the value Value interface{} // the value
} }
type LDAPRoles struct {
Members map[string][]string
}
// FindOIDCClaims finds all OIDC claims for a user. // FindOIDCClaims finds all OIDC claims for a user.
func (cli *Client) FindOIDCClaims(ctx context.Context, username string) ([]LDAPClaim, error) { func (cli *Client) FindOIDCClaims(ctx context.Context, username string) ([]LDAPClaim, error) {
if username == "" { if username == "" {
@ -213,12 +193,11 @@ func (cli *Client) FindOIDCClaims(ctx context.Context, username string) ([]LDAPC
roles := make(map[string]interface{}) roles := make(map[string]interface{})
for _, entry := range entries { for _, entry := range entries {
roleDNs, ok := entry["dn"] roleDN, ok := entry["dn"].(string)
if !ok || len(roleDNs) == 0 { if !ok || roleDN == "" {
log.Infow("No required LDAP attribute for a role", "ldapAttribute", "dn", "entry", entry) log.Infow("No required LDAP attribute for a role", "ldapAttribute", "dn", "entry", entry)
continue continue
} }
roleDN := roleDNs[0]
if entry[cli.RoleAttr] == nil { if entry[cli.RoleAttr] == nil {
log.Infow("No required LDAP attribute for a role", "ldapAttribute", cli.RoleAttr, "roleDN", roleDN) log.Infow("No required LDAP attribute for a role", "ldapAttribute", cli.RoleAttr, "roleDN", roleDN)
continue continue
@ -299,79 +278,8 @@ func (cli *Client) connect(ctx context.Context) <-chan conn {
return ch return ch
} }
func (cli *Client) findRoles(cn conn, attrs ...string) (map[string]LDAPRoles, error) {
if cli.BindDN != "" {
// We need to login to a LDAP server with a service account for retrieving user data.
if err := cn.Bind(cli.BindDN, cli.BindPass); err != nil {
return map[string]LDAPRoles{}, errors.New(err.Error() + " : failed to login to a LDAP woth a service account")
}
}
entries, err := cn.SearchRoles(attrs...)
fmt.Println("entries", entries)
if err != nil {
return map[string]LDAPRoles{}, err
}
claims := map[string]LDAPRoles{}
for _, entry := range entries {
roleDNs, ok := entry["dn"]
if !ok || len(roleDNs) == 0 {
continue
}
roleDN := roleDNs[0]
// Ensure that a role's DN is inside of the role's base DN.
// It's sufficient to compare the DN's suffix with the base DN.
n, k := len(roleDN), len(cli.RoleBaseDN)
if n < k || !strings.EqualFold(roleDN[n-k:], cli.RoleBaseDN) {
panic("You should never see that")
}
// The DN without the role's base DN must contain a CN and OU
// where the CN is for uniqueness only, and the OU is an application id.
path := strings.Split(roleDN[:n-k-1], ",")
if len(path) != 2 {
continue
}
appID := path[1][len("OU="):]
if _, ok := claims[appID]; !ok {
claims[appID] = LDAPRoles{
Members: map[string][]string{},
}
}
role := path[0][len("cn="):]
if claims[appID].Members[role] == nil {
claims[appID].Members[role] = []string{}
}
fmt.Println("entry", entry)
memberDNs, ok := entry["member"]
for _, memberDN := range memberDNs {
if !ok || memberDN == "" {
continue
}
path = strings.Split(memberDN[:n-k-1], ",")
if len(path) < 1 {
continue
}
member := strings.Split(path[0][len("uid="):], ",")
claims[appID].Members[role] = append(claims[appID].Members[role], member[0])
}
memberDNs, ok = entry["uniqueMember"]
for _, memberDN := range memberDNs {
if !ok || memberDN == "" {
continue
}
path = strings.Split(memberDN[:n-k-1], ",")
if len(path) < 1 {
continue
}
member := strings.Split(path[0][len("uid="):], ",")
claims[appID].Members[role] = append(claims[appID].Members[role], member[0])
}
}
return claims, nil
}
// findBasicUserDetails finds user's LDAP attributes that were specified. It returns nil if no such user. // findBasicUserDetails finds user's LDAP attributes that were specified. It returns nil if no such user.
func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string) (map[string][]string, error) { func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string) (map[string]interface{}, error) {
if cli.BindDN != "" { if cli.BindDN != "" {
// We need to login to a LDAP server with a service account for retrieving user data. // We need to login to a LDAP server with a service account for retrieving user data.
if err := cn.Bind(cli.BindDN, cli.BindPass); err != nil { if err := cn.Bind(cli.BindDN, cli.BindPass); err != nil {
@ -390,7 +298,7 @@ func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string
var ( var (
entry = entries[0] entry = entries[0]
details = make(map[string][]string) details = make(map[string]interface{})
) )
for _, attr := range attrs { for _, attr := range attrs {
if v, ok := entry[attr]; ok { if v, ok := entry[attr]; ok {
@ -441,40 +349,35 @@ func (c *ldapConn) Bind(bindDN, password string) error {
return err return err
} }
func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string][]string, error) { func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) {
query := fmt.Sprintf( query := fmt.Sprintf(
"(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))"+ "(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))"+
"(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))", user) "(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))", user)
return c.searchEntries(c.BaseDN, query, attrs) return c.searchEntries(c.BaseDN, query, attrs)
} }
func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string][]string, error) { func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) {
query := fmt.Sprintf("(|"+ query := fmt.Sprintf("(|"+
"(&(|(objectClass=group)(objectClass=groupOfNames)(objectClass=groupofnames))(member=%[1]s))"+ "(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))"+
"(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s))"+ "(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s))"+
")", user) ")", user)
return c.searchEntries(c.RoleBaseDN, query, attrs) return c.searchEntries(c.RoleBaseDN, query, attrs)
} }
func (c *ldapConn) SearchRoles(attrs ...string) ([]map[string][]string, error) {
query := "(|(&(|(objectClass=group)(objectClass=groupOfNames)(objectClass=groupofnames))))"
return c.searchEntries(c.RoleBaseDN, query, attrs)
}
// searchEntries executes a LDAP query, and returns a result as entries where each entry is mapping of LDAP attributes. // searchEntries executes a LDAP query, and returns a result as entries where each entry is mapping of LDAP attributes.
func (c *ldapConn) searchEntries(baseDN, query string, attrs []string) ([]map[string][]string, error) { func (c *ldapConn) searchEntries(baseDN, query string, attrs []string) ([]map[string]interface{}, error) {
req := ldap.NewSearchRequest(baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, query, attrs, nil) req := ldap.NewSearchRequest(baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, query, attrs, nil)
res, err := c.Search(req) res, err := c.Search(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entries []map[string][]string var entries []map[string]interface{}
for _, v := range res.Entries { for _, v := range res.Entries {
entry := map[string][]string{"dn": []string{v.DN}} entry := map[string]interface{}{"dn": v.DN}
for _, attr := range v.Attributes { for _, attr := range v.Attributes {
// We need the first value only for the named attribute. // We need the first value only for the named attribute.
entry[attr.Name] = attr.Values entry[attr.Name] = attr.Values[0]
} }
entries = append(entries, entry) entries = append(entries, entry)
} }

View File

@ -8,7 +8,7 @@ import (
// Tokenizer interface // Tokenizer interface
type ClaimService interface { type ClaimService interface {
AddClaimsToToken(clientID string, userId string, peer *peer.Peer) Claims AddClaimsToToken(userId string, peer *peer.Peer) Claims
DecodeClaimsInToken(host string, method string, forward string, sessionClaims Claims, publicKey string, external bool) (bool, error) DecodeClaimsInToken(host string, method string, forward string, sessionClaims Claims, publicKey string, external bool) (bool, error)
} }

View File

@ -4,7 +4,6 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure/perms_connectors" "oc-auth/infrastructure/perms_connectors"
"oc-auth/infrastructure/utils" "oc-auth/infrastructure/utils"
@ -120,23 +119,21 @@ func (h HydraClaims) DecodeClaimsInToken(host string, method string, forward str
Relation: "permits" + strings.ToUpper(meth.String()), Relation: "permits" + strings.ToUpper(meth.String()),
Object: p.(string), Object: p.(string),
} }
return perms_connectors.GetPermissionConnector("").CheckPermission(perm, nil, true), nil return perms_connectors.GetPermissionConnector().CheckPermission(perm, nil, true), nil
} }
} }
return false, errors.New("no permission found") return false, errors.New("no permission found")
} }
// add claims to token method of HydraTokenizer // add claims to token method of HydraTokenizer
func (h HydraClaims) AddClaimsToToken(clientID string, userId string, p *peer.Peer) Claims { func (h HydraClaims) AddClaimsToToken(userId string, p *peer.Peer) Claims {
claims := Claims{} claims := Claims{}
perms, err := perms_connectors.KetoConnector{}.GetPermissionByUser(userId, true) perms, err := perms_connectors.KetoConnector{}.GetPermissionByUser(userId, true)
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.AccessToken = make(map[string]interface{}) claims.Session.AccessToken = make(map[string]interface{})
claims.Session.IDToken = make(map[string]interface{}) claims.Session.IDToken = make(map[string]interface{})
fmt.Println("PERMS err 1", perms, err)
for _, perm := range perms { for _, perm := range perms {
key, err := h.generateKey(strings.ReplaceAll(perm.Relation, "permits", ""), perm.Subject) key, err := h.generateKey(strings.ReplaceAll(perm.Relation, "permits", ""), perm.Subject)
if err != nil { if err != nil {
@ -148,15 +145,15 @@ func (h HydraClaims) AddClaimsToToken(clientID string, userId string, p *peer.Pe
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.IDToken["username"] = userId
claims.Session.IDToken["peer_id"] = p.UUID claims.Session.IDToken["peer_id"] = p.UUID
// we should get group from user // we should get group from user
groups, err := perms_connectors.KetoConnector{}.GetGroupByUser(userId) groups, err := perms_connectors.KetoConnector{}.GetGroupByUser(userId)
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.IDToken["client_id"] = clientID
claims.Session.IDToken["groups"] = groups claims.Session.IDToken["groups"] = groups
claims.Session.IDToken["signature"] = sign claims.Session.IDToken["signature"] = sign
return claims return claims
} }
// add signature in the token MISSING

View File

@ -10,8 +10,8 @@ func GetAuthConnector() auth_connectors.AuthConnector {
return auth_connectors.GetAuthConnector() return auth_connectors.GetAuthConnector()
} }
func GetPermissionConnector(client string) perms_connectors.PermConnector { func GetPermissionConnector() perms_connectors.PermConnector {
return perms_connectors.GetPermissionConnector(client) return perms_connectors.GetPermissionConnector()
} }
func GetClaims() claims.ClaimService { func GetClaims() claims.ClaimService {

View File

@ -6,29 +6,24 @@ import (
"fmt" "fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure/utils" "oc-auth/infrastructure/utils"
"strings"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
) )
type KetoConnector struct { type KetoConnector struct{}
Client string
}
func (k KetoConnector) SetClient(client string) {
k.Client = client
}
func (k KetoConnector) namespace() string { func (k KetoConnector) namespace() string {
return "open-cloud" return "open-cloud"
} }
func (k KetoConnector) scope() string { func (k KetoConnector) scope() string {
return "oc-auth-realm" return "oc-auth"
} }
func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string { func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string {
n := "?namespace=" + f.namespace() n := "?namespace=" + perm.Namespace()
if perm.Object != "" { if perm.Object != "" {
n += "&object=" + perm.Object n += "&object=" + perm.Object
} }
@ -194,7 +189,6 @@ func (k KetoConnector) GetPermissionByRole(roleID string) ([]Permission, error)
} }
func (k KetoConnector) GetPermissionByUser(userID string, internal bool) ([]Permission, error) { func (k KetoConnector) GetPermissionByUser(userID string, internal bool) ([]Permission, error) {
roles, err := k.get("", "member", userID) roles, err := k.get("", "member", userID)
fmt.Println("ROLES", roles, err)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -241,7 +235,7 @@ func (k KetoConnector) get(object string, relation string, subject string) ([]Pe
return t, nil return t, nil
} }
func (k KetoConnector) binds(object string, relation string, subject string) (string, int, error) { func (k KetoConnector) binds(subject string, relation string, object string) (string, int, error) {
_, code, err := k.createRelationShip(object, relation, subject, nil) _, code, err := k.createRelationShip(object, relation, subject, nil)
if err != nil { if err != nil {
return object, code, err return object, code, err
@ -250,7 +244,6 @@ func (k KetoConnector) binds(object string, relation string, subject string) (st
} }
func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) { func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) {
fmt.Println("BIND ROLE", userID, roleID)
return k.binds(userID, "member", roleID) return k.binds(userID, "member", roleID)
} }
@ -331,6 +324,9 @@ func (k KetoConnector) UnBindPermission(roleID string, permID string, relation s
} }
func (k KetoConnector) createRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) { func (k KetoConnector) createRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) {
exist, err := k.get(object, relation, subject) exist, err := k.get(object, relation, subject)
if strings.Contains(subject, "/workflow/:id") {
fmt.Println("subject", subject, relation, exist, err)
}
if err == nil && len(exist) > 0 { if err == nil && len(exist) > 0 {
return nil, 409, errors.New("Relation already exist") return nil, 409, errors.New("Relation already exist")
} }
@ -342,11 +338,11 @@ func (k KetoConnector) createRelationShip(object string, relation string, subjec
if err != nil { if err != nil {
return nil, code, err return nil, code, err
} }
body["subject_set"] = map[string]interface{}{"namespace": k.namespace(), "object": s.Object, "relation": s.Relation, "subject_id": s.Subject} body["subject_set"] = map[string]interface{}{"namespace": s.Namespace(), "object": s.Object, "relation": s.Relation, "subject_id": s.Subject}
} }
host := conf.GetConfig().PermissionConnectorHost host := conf.GetConfig().PermissionConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorAdminPort) port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorAdminPort)
b, err := caller.CallPut("http://"+host+":"+port, "/relation-tuples", body) b, err := caller.CallPut("http://"+host+":"+port, "/admin/relation-tuples", body)
if err != nil { if err != nil {
log := oclib.GetLogger() log := oclib.GetLogger()
log.Error().Msg(err.Error()) log.Error().Msg(err.Error())

View File

@ -1,8 +1,6 @@
package perms_connectors package perms_connectors
import ( import (
"oc-auth/conf"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
) )
@ -23,7 +21,6 @@ func (k Permission) Scope() string {
type PermConnector interface { type PermConnector interface {
Status() tools.State Status() tools.State
SetClient(scope string)
CheckPermission(perm Permission, permDependancies *Permission, internal bool) bool CheckPermission(perm Permission, permDependancies *Permission, internal bool) bool
BindRole(userID string, roleID string) (string, int, error) BindRole(userID string, roleID string) (string, int, error)
BindGroup(userID string, groupID string) (string, int, error) BindGroup(userID string, groupID string) (string, int, error)
@ -54,6 +51,6 @@ var c = map[string]PermConnector{
"keto": KetoConnector{}, "keto": KetoConnector{},
} }
func GetPermissionConnector(scope string) PermConnector { func GetPermissionConnector() PermConnector {
return c[conf.GetConfig().PermissionConnectorHost] return c["keto"]
} }

View File

@ -1,78 +0,0 @@
version: "3"
services:
hydra-client-2:
image: oryd/hydra:v2.2.0
container_name: hydra-client-2
environment:
HYDRA_ADMIN_URL: http://hydra-2:4445
ORY_SDK_URL: http://hydra-2:4445
command:
- create
- oauth2-client
- --skip-tls-verify
- --name
- test-client
- --secret
- oc-auth-got-secret
- --response-type
- id_token,token,code
- --grant-type
- implicit,refresh_token,authorization_code,client_credentials
- --scope
- openid,profile,email,roles
- --token-endpoint-auth-method
- client_secret_post
- --redirect-uri
- http://localhost:3000
networks:
- hydra-net
- catalog
deploy:
restart_policy:
condition: none
depends_on:
- hydra-2
healthcheck:
test: ["CMD", "curl", "-f", "http://hydra-2:4445"]
interval: 10s
timeout: 10s
retries: 10
hydra-2:
container_name: hydra-2
image: oryd/hydra:v2.2.0
environment:
SECRETS_SYSTEM: oc-auth-got-secret
LOG_LEAK_SENSITIVE_VALUES: true
URLS_SELF_ISSUER: http://hydra-2:4444
URLS_SELF_PUBLIC: http://hydra-2:4444
WEBFINGER_OIDC_DISCOVERY_SUPPORTED_SCOPES: profile,email,phone,roles
WEBFINGER_OIDC_DISCOVERY_SUPPORTED_CLAIMS: name,family_name,given_name,nickname,email,phone_number
DSN: memory
command: serve all --dev
networks:
- hydra-net
- catalog
ports:
- "4446:4444"
- "4447:4445"
deploy:
restart_policy:
condition: on-failure
ldap-2:
image: pgarrett/ldap-alpine
container_name: ldap-2
volumes:
- "./ldap-2.ldif:/ldif/ldap.ldif"
networks:
- hydra-net
- catalog
ports:
- "389:389"
deploy:
restart_policy:
condition: on-failure
networks:
hydra-net:
catalog:
external: true

View File

@ -1,24 +0,0 @@
dn: uid=admin2,ou=Users,dc=example,dc=com
objectClass: inetOrgPerson
cn: Admin2
sn: Istrator
uid: admin2
userPassword: admin2
mail: admin2@example.com
ou: Users
dn: ou=AppRoles,dc=example,dc=com
objectClass: organizationalunit
ou: AppRoles
description: AppRoles
dn: ou=App1,ou=AppRoles,dc=example,dc=com
objectClass: organizationalunit
ou: App1
description: App1
dn: cn=traveler,ou=App1,ou=AppRoles,dc=example,dc=com
objectClass: groupofnames
cn: traveler
description: traveler
member: uid=admin2,ou=Users,dc=example,dc=com

48
main.go
View File

@ -1,12 +1,9 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure" "oc-auth/infrastructure"
auth_connectors "oc-auth/infrastructure/auth_connector"
_ "oc-auth/routers" _ "oc-auth/routers"
"os" "os"
"strconv" "strconv"
@ -17,6 +14,7 @@ import (
"cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/filter/cors"
) )
const appname = "oc-auth" const appname = "oc-auth"
@ -35,9 +33,11 @@ func main() {
conf.GetConfig().PublicKeyPath = o.GetStringDefault("PUBLIC_KEY_PATH", "./pem/public.pem") conf.GetConfig().PublicKeyPath = o.GetStringDefault("PUBLIC_KEY_PATH", "./pem/public.pem")
conf.GetConfig().PrivateKeyPath = o.GetStringDefault("PRIVATE_KEY_PATH", "./pem/private.pem") conf.GetConfig().PrivateKeyPath = o.GetStringDefault("PRIVATE_KEY_PATH", "./pem/private.pem")
conf.GetConfig().ClientSecret = o.GetStringDefault("CLIENT_SECRET", "oc-auth-got-secret") conf.GetConfig().ClientSecret = o.GetStringDefault("CLIENT_SECRET", "oc-auth-got-secret")
conf.GetConfig().OAuth2ClientSecretName = o.GetStringDefault("OAUTH2_CLIENT_SECRET_NAME", "oc-oauth2-client-secret")
conf.GetConfig().OAuth2ClientSecretNamespace = o.GetStringDefault("NAMESPACE", "default")
conf.GetConfig().Auth = o.GetStringDefault("AUTH", "hydra") conf.GetConfig().Auth = o.GetStringDefault("AUTH", "hydra")
conf.GetConfig().AuthConnectorHost = o.GetStringDefault("AUTH_CONNECTOR_HOST", "localhost") conf.GetConfig().AuthConnectorHost = o.GetStringDefault("AUTH_CONNECTOR_HOST", "localhost")
conf.GetConfig().AuthConnectPublicHost = o.GetStringDefault("AUTH_CONNECTOR_PUBLIC_HOST", "localhost")
conf.GetConfig().AuthConnectorPort = o.GetIntDefault("AUTH_CONNECTOR_PORT", 4444) conf.GetConfig().AuthConnectorPort = o.GetIntDefault("AUTH_CONNECTOR_PORT", 4444)
conf.GetConfig().AuthConnectorAdminPort = o.GetIntDefault("AUTH_CONNECTOR_ADMIN_PORT", 4445) conf.GetConfig().AuthConnectorAdminPort = o.GetIntDefault("AUTH_CONNECTOR_ADMIN_PORT", 4445)
conf.GetConfig().PermissionConnectorHost = o.GetStringDefault("PERMISSION_CONNECTOR_HOST", "keto") conf.GetConfig().PermissionConnectorHost = o.GetStringDefault("PERMISSION_CONNECTOR_HOST", "keto")
@ -45,7 +45,6 @@ func main() {
conf.GetConfig().PermissionConnectorAdminPort = o.GetIntDefault("PERMISSION_CONNECTOR_ADMIN_PORT", 4467) conf.GetConfig().PermissionConnectorAdminPort = o.GetIntDefault("PERMISSION_CONNECTOR_ADMIN_PORT", 4467)
// config LDAP // config LDAP
conf.GetConfig().SourceMode = o.GetStringDefault("SOURCE_MODE", "ldap")
conf.GetConfig().LDAPEndpoints = o.GetStringDefault("LDAP_ENDPOINTS", "ldap:389") conf.GetConfig().LDAPEndpoints = o.GetStringDefault("LDAP_ENDPOINTS", "ldap:389")
conf.GetConfig().LDAPBindDN = o.GetStringDefault("LDAP_BINDDN", "cn=admin,dc=example,dc=com") conf.GetConfig().LDAPBindDN = o.GetStringDefault("LDAP_BINDDN", "cn=admin,dc=example,dc=com")
conf.GetConfig().LDAPBindPW = o.GetStringDefault("LDAP_BINDPW", "password") conf.GetConfig().LDAPBindPW = o.GetStringDefault("LDAP_BINDPW", "password")
@ -55,36 +54,17 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
generateRole()
discovery() discovery()
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Authorization", "Content-Type"},
ExposeHeaders: []string{"Content-Length", "Content-Type"},
AllowCredentials: true,
}))
beego.Run() beego.Run()
} }
func generateRole() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
// if from ldap, create roles from ldap
if conf.GetConfig().SourceMode == "ldap" {
ldap := auth_connectors.New()
roles, err := ldap.GetRoles(context.Background())
if err != nil {
panic(err)
}
fmt.Println("ROLE", roles)
for _, role := range roles {
for r, m := range role.Members {
infrastructure.GetPermissionConnector("").CreateRole(r)
for _, p := range m {
infrastructure.GetPermissionConnector("").BindRole(r, p)
}
}
}
}
}
func generateSelfPeer() error { func generateSelfPeer() error {
// TODO check if files at private & public path are set // TODO check if files at private & public path are set
// check if files at private & public path are set // check if files at private & public path are set
@ -95,7 +75,7 @@ func generateSelfPeer() error {
return errors.New("public key path does not exist") return errors.New("public key path does not exist")
} }
// check if peer already exists // check if peer already exists
p := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), false) p := oclib.Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), oclib.LibDataEnum(oclib.PEER))
file := "" file := ""
f, err := os.ReadFile(conf.GetConfig().PublicKeyPath) f, err := os.ReadFile(conf.GetConfig().PublicKeyPath)
if err != nil { if err != nil {
@ -120,7 +100,7 @@ func generateSelfPeer() error {
PublicKey: file, PublicKey: file,
State: peer.SELF, State: peer.SELF,
} }
data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).StoreOne(peer.Serialize(peer)) data := oclib.StoreOne(oclib.LibDataEnum(oclib.PEER), peer.Serialize())
if data.Err != "" { if data.Err != "" {
return errors.New(data.Err) return errors.New(data.Err)
} }
@ -129,7 +109,7 @@ func generateSelfPeer() error {
func discovery() { func discovery() {
api := tools.API{} api := tools.API{}
conn := infrastructure.GetPermissionConnector("") conn := infrastructure.GetPermissionConnector()
conn.CreateRole(conf.GetConfig().AdminRole) conn.CreateRole(conf.GetConfig().AdminRole)
conn.BindRole(conf.GetConfig().AdminRole, "admin") conn.BindRole(conf.GetConfig().AdminRole, "admin")

BIN
oc-auth

Binary file not shown.

View File

@ -81,7 +81,7 @@ func init() {
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "InternaisDraftlAuthForward", Method: "InternalAuthForward",
Router: `/forward`, Router: `/forward`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
@ -99,8 +99,8 @@ func init() {
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Login", Method: "LoginLDAP",
Router: `/login`, Router: `/ldap/login`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
@ -108,8 +108,8 @@ func init() {
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "LogOut", Method: "LogOutLDAP",
Router: `/logout`, Router: `/ldap/logout`,
AllowHTTPMethods: []string{"delete"}, AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,

View File

@ -191,7 +191,7 @@
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "user_id", "name": "group_id",
"description": "The group_id you want to unbind", "description": "The group_id you want to unbind",
"required": true, "required": true,
"type": "string" "type": "string"
@ -233,7 +233,7 @@
} }
} }
}, },
"/login": { "/ldap/login": {
"post": { "post": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@ -249,13 +249,6 @@
"schema": { "schema": {
"$ref": "#/definitions/models.workflow" "$ref": "#/definitions/models.workflow"
} }
},
{
"in": "query",
"name": "client_id",
"description": "the client_id you want to get",
"required": true,
"type": "string"
} }
], ],
"responses": { "responses": {
@ -265,7 +258,7 @@
} }
} }
}, },
"/logout": { "/ldap/logout": {
"delete": { "delete": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@ -278,13 +271,6 @@
"name": "Authorization", "name": "Authorization",
"description": "auth token", "description": "auth token",
"type": "string" "type": "string"
},
{
"in": "query",
"name": "client_id",
"description": "the client_id you want to get",
"required": true,
"type": "string"
} }
], ],
"responses": { "responses": {
@ -479,13 +465,6 @@
"schema": { "schema": {
"$ref": "#/definitions/models.Token" "$ref": "#/definitions/models.Token"
} }
},
{
"in": "query",
"name": "client_id",
"description": "the client_id you want to get",
"required": true,
"type": "string"
} }
], ],
"responses": { "responses": {

View File

@ -119,7 +119,7 @@ paths:
operationId: GroupController.UnBind operationId: GroupController.UnBind
parameters: parameters:
- in: path - in: path
name: user_id name: group_id
description: The group_id you want to unbind description: The group_id you want to unbind
required: true required: true
type: string type: string
@ -175,7 +175,7 @@ paths:
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
/login: /ldap/login:
post: post:
tags: tags:
- oc-auth/controllersOAuthController - oc-auth/controllersOAuthController
@ -190,15 +190,10 @@ paths:
required: true required: true
schema: schema:
$ref: '#/definitions/models.workflow' $ref: '#/definitions/models.workflow'
- in: query
name: client_id
description: the client_id you want to get
required: true
type: string
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
/logout: /ldap/logout:
delete: delete:
tags: tags:
- oc-auth/controllersOAuthController - oc-auth/controllersOAuthController
@ -211,11 +206,6 @@ paths:
name: Authorization name: Authorization
description: auth token description: auth token
type: string type: string
- in: query
name: client_id
description: the client_id you want to get
required: true
type: string
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
@ -360,11 +350,6 @@ paths:
required: true required: true
schema: schema:
$ref: '#/definitions/models.Token' $ref: '#/definitions/models.Token'
- in: query
name: client_id
description: the client_id you want to get
required: true
type: string
responses: responses:
"200": "200":
description: '{string}' description: '{string}'