10 Commits

Author SHA1 Message Date
mr
0989aeb979 neo oc-lib 2025-02-06 08:56:30 +01:00
mr
8f4e33ab80 neo oc lib 2025-02-05 08:43:17 +01:00
mr
b84c2ef353 workin oc-auth 2025-01-17 17:24:08 +01:00
mr
fd65220b91 add groups in claims 2024-11-27 12:36:37 +01:00
mr
1722980514 simplify code 2024-11-27 11:54:25 +01:00
mr
01daaae766 Add Group To Keto 2024-11-27 11:12:46 +01:00
mr
be071ec328 oclib + perms naming 2024-11-21 11:07:19 +01:00
mr
9a86604564 test 2024-11-18 15:16:58 +01:00
mr
cc91341547 public key bug 2024-11-18 14:58:26 +01:00
mr
2a8349b0c7 new peer url 2024-11-15 09:40:24 +01:00
24 changed files with 1385 additions and 142 deletions

View File

@@ -3,6 +3,7 @@ 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

221
controllers/group.go Normal file
View File

@@ -0,0 +1,221 @@
package controllers
import (
"oc-auth/infrastructure"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about auth
type GroupController struct {
beego.Controller
}
// @Title Create
// @Description create group
// @Param id path string true "the id you want to get"
// @Success 200 {auth} create success!
// @router /:id [post]
func (o *GroupController) Post() {
// store and return Id or post with UUID
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
group, code, err := infrastructure.GetPermissionConnector(clientID).CreateGroup(id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title GetByUser
// @Description find group by user id
// @Param id path string true "the id you want to get"
// @Success 200 {auth} string
// @router /user/:id [get]
func (o *GroupController) GetByUser() {
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
group, err := infrastructure.GetPermissionConnector(clientID).GetGroupByUser(id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title GetAll
// @Description find groups
// @Success 200 {group} string
// @router / [get]
func (o *GroupController) GetAll() {
clientID := ExtractClient(*o.Ctx.Request)
group, err := infrastructure.GetPermissionConnector(clientID).GetGroup("")
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Get
// @Description find group by id
// @Param id path string true "the id you want to get"
// @Success 200 {group} string
// @router /:id [get]
func (o *GroupController) Get() {
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
group, err := infrastructure.GetPermissionConnector(clientID).GetGroup(id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Delete
// @Description delete the group
// @Param id path string true "The id you want to delete"
// @Success 200 {string} delete success!
// @router /:id [delete]
func (o *GroupController) Delete() {
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
group, code, err := infrastructure.GetPermissionConnector(clientID).DeleteGroup(id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Clear
// @Description clear the group
// @Success 200 {string} delete success!
// @router /clear [delete]
func (o *GroupController) Clear() {
clientID := ExtractClient(*o.Ctx.Request)
group, code, err := infrastructure.GetPermissionConnector(clientID).DeleteGroup("")
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Bind
// @Description bind the group to user
// @Param user_id path string true "The user_id you want to bind"
// @Param group_id path string true "The group_id you want to bind"
// @Success 200 {string} bind success!
// @router /:user_id/:group_id [post]
func (o *GroupController) Bind() {
user_id := o.Ctx.Input.Param(":user_id")
group_id := o.Ctx.Input.Param(":group_id")
clientID := ExtractClient(*o.Ctx.Request)
group, code, err := infrastructure.GetPermissionConnector(clientID).BindGroup(user_id, group_id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title UnBind
// @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 user_id you want to unbind"
// @Success 200 {string} bind success!
// @router /:user_id/:group_id [delete]
func (o *GroupController) UnBind() {
user_id := o.Ctx.Input.Param(":user_id")
group_id := o.Ctx.Input.Param(":group_id")
clientID := ExtractClient(*o.Ctx.Request)
group, code, err := infrastructure.GetPermissionConnector(clientID).UnBindGroup(user_id, group_id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": group,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}

View File

@@ -1,9 +1,11 @@
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"
@@ -22,10 +24,12 @@ 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 /ldap/logout [delete] // @router /logout [delete]
func (o *OAuthController) LogOutLDAP() { func (o *OAuthController) LogOut() {
// 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 {
@@ -36,7 +40,7 @@ func (o *OAuthController) LogOutLDAP() {
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(reqToken) token, err := infrastructure.GetAuthConnector().Logout(clientID, reqToken)
if err != nil || token == nil { if err != nil || token == nil {
o.Data["json"] = err o.Data["json"] = err
} else { } else {
@@ -48,25 +52,33 @@ func (o *OAuthController) LogOutLDAP() {
// @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 /ldap/login [post] // @router /login [post]
func (o *OAuthController) LoginLDAP() { func (o *OAuthController) Login() {
// 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)
fmt.Println("found", found, "err", err)
if err != nil || !found { if err != nil || !found {
o.Data["json"] = err o.Data["json"] = err
o.Ctx.ResponseWriter.WriteHeader(401) o.Ctx.ResponseWriter.WriteHeader(401)
o.ServeJSON() o.ServeJSON()
return return
} }
token, err := infrastructure.GetAuthConnector().Login(res.Username, }
token, err := infrastructure.GetAuthConnector().Login(
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)
@@ -79,13 +91,15 @@ func (o *OAuthController) LoginLDAP() {
// @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(&token) newToken, err := infrastructure.GetAuthConnector().Refresh(clientID, &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)
@@ -128,7 +142,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) InternalAuthForward() { func (o *OAuthController) InternaisDraftlAuthForward() {
fmt.Println("InternalAuthForward") fmt.Println("InternalAuthForward")
reqToken := o.Ctx.Request.Header.Get("Authorization") reqToken := o.Ctx.Request.Header.Get("Authorization")
if reqToken == "" { if reqToken == "" {
@@ -149,7 +163,7 @@ func (o *OAuthController) InternalAuthForward() {
} else { } else {
reqToken = splitToken[1] reqToken = splitToken[1]
} }
origin, publicKey, external := o.extractOrigin() origin, publicKey, external := o.extractOrigin(o.Ctx.Request)
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"),
@@ -161,7 +175,8 @@ func (o *OAuthController) InternalAuthForward() {
o.ServeJSON() o.ServeJSON()
} }
func (o *OAuthController) extractOrigin() (string, string, bool) { func (o *OAuthController) extractOrigin(request *http.Request) (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")
@@ -174,7 +189,7 @@ func (o *OAuthController) extractOrigin() (string, string, bool) {
if t != "" { if t != "" {
searchStr = strings.Replace(searchStr, t, "", -1) searchStr = strings.Replace(searchStr, t, "", -1)
} }
peer := oclib.Search(nil, searchStr, oclib.LibDataEnum(oclib.PEER)) peer := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), user, peerID, groups, nil).Search(nil, searchStr, false)
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
} }
@@ -190,3 +205,29 @@ func (o *OAuthController) extractOrigin() (string, string, bool) {
} }
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,7 +16,8 @@ type PermissionController struct {
// @Success 200 {permission} string // @Success 200 {permission} string
// @router / [get] // @router / [get]
func (o *PermissionController) GetAll() { func (o *PermissionController) GetAll() {
role, err := infrastructure.GetPermissionConnector().GetPermission("", "") clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -41,7 +42,8 @@ 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")
role, err := infrastructure.GetPermissionConnector().GetPermissionByRole(id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -66,7 +68,8 @@ 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")
role, err := infrastructure.GetPermissionConnector().GetPermissionByUser(id, true) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -92,7 +95,8 @@ 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")
role, err := infrastructure.GetPermissionConnector().GetPermission(id, rel) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -115,7 +119,8 @@ 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() {
role, code, err := infrastructure.GetPermissionConnector().DeletePermission("", "", true) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -144,7 +149,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().BindPermission(role_id, permission_id, rel) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -173,7 +179,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().UnBindPermission(role_id, permission_id, rel) clientID := ExtractClient(*o.Ctx.Request)
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,7 +19,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().CreateRole(id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -44,7 +45,8 @@ 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")
role, err := infrastructure.GetPermissionConnector().GetRoleByUser(id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -67,7 +69,8 @@ func (o *RoleController) GetByUser() {
// @Success 200 {role} string // @Success 200 {role} string
// @router / [get] // @router / [get]
func (o *RoleController) GetAll() { func (o *RoleController) GetAll() {
role, err := infrastructure.GetPermissionConnector().GetRole("") clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -92,7 +95,8 @@ 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")
role, err := infrastructure.GetPermissionConnector().GetRole(id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -117,7 +121,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().DeleteRole(id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -140,7 +145,8 @@ 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() {
role, code, err := infrastructure.GetPermissionConnector().DeleteRole("") clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -167,7 +173,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().BindRole(user_id, role_id) clientID := ExtractClient(*o.Ctx.Request)
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,
@@ -194,7 +201,8 @@ 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")
role, code, err := infrastructure.GetPermissionConnector().UnBindRole(user_id, role_id) clientID := ExtractClient(*o.Ctx.Request)
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,

21
docker-compose-2.yml Normal file
View File

@@ -0,0 +1,21 @@
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

5
go.mod
View File

@@ -3,7 +3,7 @@ module oc-auth
go 1.22.0 go 1.22.0
require ( require (
cloud.o-forge.io/core/oc-lib v0.0.0-20241108104423-7fd44a55cb28 cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f
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/nats-io/nats.go v1.37.0
github.com/ory/hydra-client-go v1.11.8 github.com/ory/hydra-client-go v1.11.8
@@ -15,6 +15,7 @@ require (
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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/biter777/countries v1.7.5 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
@@ -36,6 +37,7 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/marcinwyszynski/geopoint v0.0.0-20140302213024-cf2a6f750c5b // indirect
github.com/mattn/goveralls v0.0.12 // indirect github.com/mattn/goveralls v0.0.12 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect github.com/openzipkin/zipkin-go v0.4.1 // indirect
@@ -44,6 +46,7 @@ require (
github.com/ory/x v0.0.575 // indirect github.com/ory/x v0.0.575 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // 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/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/afero v1.9.5 // indirect github.com/spf13/afero v1.9.5 // indirect

212
go.sum
View File

@@ -35,6 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.o-forge.io/core/oc-lib v0.0.0-20240904135449-4f0ab6a3760f h1:v9mw3uNg/DJswOvHooMu8/BMedA+vIXbma+8iUwsjUI=
cloud.o-forge.io/core/oc-lib v0.0.0-20240904135449-4f0ab6a3760f/go.mod h1:FIJD0taWLJ5pjQLJ6sfE2KlTkvbmk5SMcyrxdjsaVz0=
cloud.o-forge.io/core/oc-lib v0.0.0-20241016115009-b43226648692 h1:cfRhQioLwTBg9h1OOOp3VcUsBChO97M9lRDP8aq2Gkk= cloud.o-forge.io/core/oc-lib v0.0.0-20241016115009-b43226648692 h1:cfRhQioLwTBg9h1OOOp3VcUsBChO97M9lRDP8aq2Gkk=
cloud.o-forge.io/core/oc-lib v0.0.0-20241016115009-b43226648692/go.mod h1:t+zpCTVKVdHH/BImwtMYY2QIWLMXKgY4n/JhFm3Vpu8= cloud.o-forge.io/core/oc-lib v0.0.0-20241016115009-b43226648692/go.mod h1:t+zpCTVKVdHH/BImwtMYY2QIWLMXKgY4n/JhFm3Vpu8=
cloud.o-forge.io/core/oc-lib v0.0.0-20241016151521-9654d59fc076 h1:kPIBXPWdO47YdZClB/QYkt3EaReYS7Gs/c4FSXzhjtI= cloud.o-forge.io/core/oc-lib v0.0.0-20241016151521-9654d59fc076 h1:kPIBXPWdO47YdZClB/QYkt3EaReYS7Gs/c4FSXzhjtI=
@@ -79,6 +81,210 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20241107122526-f3df1e42b9ba h1:MGd8N7bY1LWXM
cloud.o-forge.io/core/oc-lib v0.0.0-20241107122526-f3df1e42b9ba/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo= cloud.o-forge.io/core/oc-lib v0.0.0-20241107122526-f3df1e42b9ba/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241108104423-7fd44a55cb28 h1:jekSPkD/b59kJ9Bp/trBWnahkdd1FkX4csQOcSaZa8I= cloud.o-forge.io/core/oc-lib v0.0.0-20241108104423-7fd44a55cb28 h1:jekSPkD/b59kJ9Bp/trBWnahkdd1FkX4csQOcSaZa8I=
cloud.o-forge.io/core/oc-lib v0.0.0-20241108104423-7fd44a55cb28/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo= cloud.o-forge.io/core/oc-lib v0.0.0-20241108104423-7fd44a55cb28/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241114103936-c24f2f26c4ed h1:vOy5nuu/sETZ+o53qfWbZqd09WT44bjS7VG24c5jtRM=
cloud.o-forge.io/core/oc-lib v0.0.0-20241114103936-c24f2f26c4ed/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241115080752-9a8625f8b409 h1:Pt9ih89OgmjnkFmRKdiMnUwYsfZcrqVqJWGNMS3Lsd4=
cloud.o-forge.io/core/oc-lib v0.0.0-20241115080752-9a8625f8b409/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120085309-08e9ee67fe96 h1:1f2m8148/bOY19urpgtgShmGPDMnnjRqcEczrkVDJBA=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120085309-08e9ee67fe96/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120093920-b49685aa8223 h1:LX04VfuXWxi+Q0lKhBBd7tfyLO3R4y8um3srRVlMbSY=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120093920-b49685aa8223/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120150854-57f18b224443 h1:cqlL4/EsqYlQ6luPBC4+6+gWNwQqWVV8DPD8O7F6yM8=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120150854-57f18b224443/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120153807-3b77c0da8352 h1:xNYjEiB/nrvXLbLcjSDfNZEPSR38/LKcsQKP/oWg5HI=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120153807-3b77c0da8352/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120160521-ac49d3324d7b h1:5prB7K0iM284VmYdoRaBMZIOEXq5S0YgTrSp4+SnZyo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241120160521-ac49d3324d7b/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121065159-d8fac883d260 h1:DSumHyw9XJQ/r+LjWa5GDkjS0ri/lFkU7oPr5vv8mws=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121065159-d8fac883d260/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121071546-e9b3a65a0ec6 h1:AdUkzaX63VF3fdloWyyWT1jLM4M1pkDLErAdHyVbsKU=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121071546-e9b3a65a0ec6/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121074503-15ca06aba883 h1:JdHJT8vuup4pJCC7rjiOe0/qD7at6400ml5zZHjEeUo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241121074503-15ca06aba883/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202081145-cb21db672bb5 h1:qxXC6fkEa8bLTo0qn3VrB55tfxyjHQQa/0n97piJhNI=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202081145-cb21db672bb5/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202121923-2ec6899a1865 h1:BhGzhy6gsEA7vthuq6KWyABsRuF4KV5NqOvfkygytGg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202121923-2ec6899a1865/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202134851-9a2ed2351d7e h1:3U5JBdQRti2OpALLPhev6lkUi1TlYHgo2ADidOAfEAs=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202134851-9a2ed2351d7e/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202152644-e2ddd7e4e6f9 h1:qUA6T5Pjq/pv6dZYH4PWktXmFiRnloDX84m1U5NhvLM=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202152644-e2ddd7e4e6f9/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202155908-599a6144803e h1:3xGLiTDTgWHIIPDZyTo/clMIj+gQxnIDSE78s9/0wNE=
cloud.o-forge.io/core/oc-lib v0.0.0-20241202155908-599a6144803e/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203073336-6042d47700fd h1:iDryCORnODgAvBe1Yi+RnIGjYgUSkAv7ZCnm+CUV18w=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203073336-6042d47700fd/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203082527-2924ccd23b5c h1:3ghuxLEI3JXicDYoFx4YnkLauLl0Nq9UErjpL/2SqEU=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203082527-2924ccd23b5c/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203090110-471e0c9d9b48 h1:kVTpROPipS4YtROH9vAGZw21OMLNR48qbYedCngGThw=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203090110-471e0c9d9b48/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203095728-ea55c94c7328 h1:7iK2HzMm0EEEF60ajUVT/6jwqIirduww5Xa3191XS4I=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203095728-ea55c94c7328/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203105751-4b88da8ff66d h1:iIo+AMQ09MshkKKN8K8pd1ooLaigAYlnUUnQAaCidLo=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203105751-4b88da8ff66d/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203115141-6681c455d8e0 h1:RnHCONn0oYbEaTN1wDIeOAEM12cCZQRtvjBCVCb0b1Y=
cloud.o-forge.io/core/oc-lib v0.0.0-20241203115141-6681c455d8e0/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241204103308-fd01f535a131 h1:FdUY8b8xTdVzQ9wlphlo8TlbQif76V9oxGDYq26TsAs=
cloud.o-forge.io/core/oc-lib v0.0.0-20241204103308-fd01f535a131/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241204111455-1fcbc7c08ab0 h1:cBr4m2tcLf+dZufrjYvhvcsSqXcRDeyhnq5c5HY15po=
cloud.o-forge.io/core/oc-lib v0.0.0-20241204111455-1fcbc7c08ab0/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20241205082103-fbbce7817b73 h1:g96KMOxdhvM7x6YFqJfd08wybRzCLEvol7HfhKJfxO4=
cloud.o-forge.io/core/oc-lib v0.0.0-20241205082103-fbbce7817b73/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250110164331-5255ffc2f728 h1:3p1G82xZmEAu2OEyY5HM42Cfbb1J887P9lSoRKNhgg8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250110164331-5255ffc2f728/go.mod h1:2IevepXviessA6m67fB6ZJhZSeEeoOYWbVqPS4dzkbg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113102407-21a7ff90104a h1:rrLSuAHI/TGOTm5d7Bffu+qf4EnmPguOll5x5nG/3Tc=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113102407-21a7ff90104a/go.mod h1:VgWEn23ddKySWXrwPMhqtiBjTJnbm5t7yWjzfvNxbbI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113114256-11905339bb24 h1:Kc51xKbnyfeafHpOJP7mWh9InNGqZUwcJR46008D+Eg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113114256-11905339bb24/go.mod h1:VgWEn23ddKySWXrwPMhqtiBjTJnbm5t7yWjzfvNxbbI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113124812-6e5c87379649 h1:dmtrmNDdTR/2R3HjaIbPdu5LZViPzigwSjU207NXCxI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113124812-6e5c87379649/go.mod h1:VgWEn23ddKySWXrwPMhqtiBjTJnbm5t7yWjzfvNxbbI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113135241-a0f436b3e162 h1:oGP40P/uUngU7stnsRdx0jwxZGc+pzLzrMlUjEBSy0M=
cloud.o-forge.io/core/oc-lib v0.0.0-20250113135241-a0f436b3e162/go.mod h1:VgWEn23ddKySWXrwPMhqtiBjTJnbm5t7yWjzfvNxbbI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114071722-1c32cd2d12df h1:T52jgXQddoxwe+embR26Fwmz4G2jkl4QpYVHGtiLUNI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114071722-1c32cd2d12df/go.mod h1:VgWEn23ddKySWXrwPMhqtiBjTJnbm5t7yWjzfvNxbbI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114081637-918006302bb4 h1:AwCbDHjvUz9iQaF7hgYWyabVF/EzSSSk5bCNgntNJ6c=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114081637-918006302bb4/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114105339-b782248da741 h1:akAQLlcAXDtUhbNHbona9xJrHCzK9jxlvsDsEpVP1fg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114105339-b782248da741/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114135055-1a4694c8913a h1:AxnecA1YKOZ81OKb1akK2Qc/0UNDUxdjSww7ALyehas=
cloud.o-forge.io/core/oc-lib v0.0.0-20250114135055-1a4694c8913a/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115082026-ad69c0495144 h1:MZ90rw4SKL0dqL/Lb+7E54vkk9fb8W6X0UJo9UW/XBk=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115082026-ad69c0495144/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115095644-be3803039583 h1:6My1sqjvqgHnC4TlE7RsZQHC8AVhad0gZl8uOvLTM9o=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115095644-be3803039583/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115102820-0e0540af43d0 h1:AcHC2WIeHOSjz5xe7OsjMi39EevxdY2O/9q0VMkDRz0=
cloud.o-forge.io/core/oc-lib v0.0.0-20250115102820-0e0540af43d0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250116091455-68f418928395 h1:u4myLPGqBbzprWHg6713k5a++4yiq1ujlVy7yrMkZ9g=
cloud.o-forge.io/core/oc-lib v0.0.0-20250116091455-68f418928395/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250116142544-a4a249bab828 h1:yMDBDTs7LECyueUfh0iug502GN8GodVpQSl/gZchUjU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250116142544-a4a249bab828/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117081640-450fab437cb7 h1:SV9U48sR09cNRl48489lQHrrKJFtTMQoQcRhmtsLTYQ=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117081640-450fab437cb7/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117090737-b990fe42d375 h1:UsPWfbVgvUcOC3BtD8B9dUQfv/FnRF4IZGrYxUJr1iM=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117090737-b990fe42d375/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117100508-d44fb976e4ff h1:GaLrVn6ame6BV7pfUB2xeHCCJLBECRiCCpPj6zteL+s=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117100508-d44fb976e4ff/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117121920-ed787683f47b h1:3wap+dPPplJkDglE5toKfdFUmjobAeIJWdiRtCQ3xkQ=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117121920-ed787683f47b/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117124801-e5c7dbe4cb96 h1:opQ/Uku27DOKAqDcKC9k6J9H5Tj9bNyKdHnJnD3U850=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117124801-e5c7dbe4cb96/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117135417-c63a1fef6c48 h1:dEebv8ZV5rt6BYPkcK6HOts+OPqkSxkKp5zn1lCq1vs=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117135417-c63a1fef6c48/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117152246-b85ca8674b27 h1:QEIj90eIoYsjs1uekbI3Nu48KDWmzGV7ugcr9agJbYI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250117152246-b85ca8674b27/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120123706-58b36f282344 h1:MPt8BhrbMJiMa4KDWqBUvdrlone7UxgIgZ5PW4du0Ek=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120123706-58b36f282344/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120124939-67b8215adf79 h1:9Y+KJlzy5jHhrd4b44pNEBjSJKnIyvlSQ5Mbj1zcXbA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120124939-67b8215adf79/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120143509-305f2605030d h1:f1tpLADIAbwTKxN62csH+v2Fe0q1eQ7dYIDhPl1GZ8I=
cloud.o-forge.io/core/oc-lib v0.0.0-20250120143509-305f2605030d/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121080257-de585a723426 h1:49cuCsDsBE6ZrvqMh6d48ZynpPyEpkw1LtC0nMQnvEU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121080257-de585a723426/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121083541-0d83885b9b5e h1:yh2tiTxuQbrdgCePREyMewPr8Btdacpw6vo7ymmqf7Y=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121083541-0d83885b9b5e/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121101118-bc12fb53be23 h1:oOSJA8w33aJ2TlMRuR7bU/rme/IYSBcVjrb6gE/jwSw=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121101118-bc12fb53be23/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121105544-bf5a16f41bea h1:X9YiXv2GSLT6jotS3C/JvvdYBLtxgKI8OV60ndJzjXk=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121105544-bf5a16f41bea/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121131007-745bb58c593e h1:rHbooeLrsMvIYj5nHc3MK8NVEh9v5edFBCkOxeRoYjs=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121131007-745bb58c593e/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121160438-67ebeca1f489 h1:XwPLFaKjP0o6ZuKnj5aDJ9hIBlX8giNS9BB78uIH0g0=
cloud.o-forge.io/core/oc-lib v0.0.0-20250121160438-67ebeca1f489/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122080653-67940296d255 h1:VFlxjrbks8pDzoZ40lnyHD5qVyEMAIfEAmY2w4wBAE8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122080653-67940296d255/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122090736-8ab313e6cbd8 h1:u7Rt0tQMCzylFPyMcO5uNQ8041K80cM0BQNbBDbjAj0=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122090736-8ab313e6cbd8/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122101635-2a93b17d71d8 h1:AvthXY1/mrB4aeQpoj84ewVCdIYYemwn9WydYJ+9hyw=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122101635-2a93b17d71d8/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122110438-062c1afe8568 h1:pk7Gqa1yEwl5ASc9wJNjxJ+1XfTXYSwDvsxB3KOHWoo=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122110438-062c1afe8568/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122121814-ed1e76105250 h1:TwCz7oXB7diECiM/kadwDZ78iM8E8ka2ShKs/PzdszA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122121814-ed1e76105250/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122135342-4be954a6f359 h1:x5dGOGYgdDhSeYtAkWeNlWQLU24yv8BUpwx1Idc9+ME=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122135342-4be954a6f359/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122140340-9c71730d9cb7 h1:oAkv9IOuiP71VO/plOkPHaPk9X3ELfnGdSz2cctLnGw=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122140340-9c71730d9cb7/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122153005-0e798dac5081 h1:P/WDRzkAJHhPuZZbU2VmVqSJ6AcMN/ia/pPZ60MpRfo=
cloud.o-forge.io/core/oc-lib v0.0.0-20250122153005-0e798dac5081/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123074822-df04133551e4 h1:ayV2U6VUUJXdBE2AGuRuwTKr7WqIycmVgEMv8v/KlGU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123074822-df04133551e4/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123082727-8cba10c4fe29 h1:zt0AA0GddWtbgupsvFvNAozrGMP0FISHnjSmsp3Ihgc=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123082727-8cba10c4fe29/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123094950-d15fdac27bde h1:Yjr0WPiR3dMg+H8EIO4GzqohRZBvGh/h4ysx5n8wCZw=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123094950-d15fdac27bde/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123103535-2205ac9b5819 h1:y/opEsKeo7G5Os2RWd7zF5i5DU4neDLt6fUq2hSW66U=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123103535-2205ac9b5819/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123114959-49e495f062eb h1:9FDB2xUhO+PFkb1mhNq+vItyfW/Jb0KjBRDEDPqPcno=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123114959-49e495f062eb/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123134717-db6049bab345 h1:OW5TLnNhNxJCkhMXUy5d9VSOgEGNFc9+uA3thyPuRA4=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123134717-db6049bab345/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123140834-c1888f89218f h1:iNqXYlnTh4nnfuVN/NObIJO5g9Mu3Mi9yFGmNFwO1Jk=
cloud.o-forge.io/core/oc-lib v0.0.0-20250123140834-c1888f89218f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250124095557-97d466818af0 h1:v8Fj897AF5l8icSm2FE0E2tkl96eJI43Zr4UHIUkL6Y=
cloud.o-forge.io/core/oc-lib v0.0.0-20250124095557-97d466818af0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127080547-fbb55e64dcf4 h1:s6+5sTIeR86N+9oK3uXItlP0L1SgKCwMNQFU6LERDU4=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127080547-fbb55e64dcf4/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127083756-68bacf5da410 h1:b+dzulgEl+a7BudsqCkgBg/1aEqo8/1WpGs+WGZHznE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127083756-68bacf5da410/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127110938-1ad9ce09cb35 h1:PWlFiCaAHTUDuwOf84hA4BDivEA3FU+DDH7dBg9IPho=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127110938-1ad9ce09cb35/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127131512-7ca360be6aa4 h1:8y8I+hmSuUPV2dt/qw6d2TY/YRLXvZp0zE9iSwR3qv4=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127131512-7ca360be6aa4/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127134257-8b03df7923bd h1:eylhA0MziFMzY+kfXy2tnZEHDWIXCh/kPDLyBG2OC5E=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127134257-8b03df7923bd/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127150345-db85d1a48b73 h1:SNwsmEyaHrnoN7/IBathlA/HI/y4D2IBJjZEdtUC7Ew=
cloud.o-forge.io/core/oc-lib v0.0.0-20250127150345-db85d1a48b73/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250128131916-598774b0b197 h1:tAi5pznkPDjCFO81EhvS8Djx1e7iz4D2e72lxegRVmQ=
cloud.o-forge.io/core/oc-lib v0.0.0-20250128131916-598774b0b197/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129073743-74a1f66d26c2 h1:ScjLqkn82u+on8CXnfgi52UZqddR879WlUtiq9qQOdo=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129073743-74a1f66d26c2/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129100135-330768490a61 h1:afATt4OzRndXApO1Xqn9PeKohW5G2nhqvptZkE2pML8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129100135-330768490a61/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129133324-ede2d5fd5322 h1:d0/n7kJZNG6QKdI5ySqYGe3nYYOKmko76ysjlZA30Dk=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129133324-ede2d5fd5322/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129143004-df2c38199cf0 h1:8cIJxCeVHbefpa7oBZPeFUAa7Mmtiw93Z1xMa9Qf/wk=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129143004-df2c38199cf0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129154925-84d20c52fa1c h1:6+KdDssQyPZSCmtiBrlygHIAt2yhewx3rz/SPEfsYnI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250129154925-84d20c52fa1c/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130072403-826d7586b127 h1:wYLo29accEk0anP8eLjBKbDyYGLFKg4Qp41NvCb2JsQ=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130072403-826d7586b127/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130084513-787c01b4be1c h1:3TEloYSf4k1o9tkEo5T3sES+qZcJBsdR82o+T81SC3A=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130084513-787c01b4be1c/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130101134-107ce2580128 h1:AElHp4SeiVmMiyCta9r8JOpSYMAS0To/fLK6eaBz1PU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130101134-107ce2580128/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130130847-976a5cedcb5f h1:0buFXek+V4E4rIGBEygLXpw34I50yAGqTIAOyTgZwsA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250130130847-976a5cedcb5f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131073800-3ec0d554edad h1:Ey6yORB8TOa+PkMpNhH0tayZuZ6FwyJ59vZM4BRGHnY=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131073800-3ec0d554edad/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131082340-892bd93471aa h1:53a/yqBAVkNpeAaCqxHx3FWC0wV5XK/dhooR3f0Kp8g=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131082340-892bd93471aa/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131100142-b2113bff62fa h1:S7nsqFotIeXSPJqipNW6wB3VsfYhFrWcZIR8mX6aJg0=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131100142-b2113bff62fa/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131110730-a2f2d0ebef72 h1:0EUj84bzUWvaH8egQkjH1xQ+HoyX9EZqtokNosYywgU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131110730-a2f2d0ebef72/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131153610-6807614ac86b h1:/SjZVsLeH8sXopUeR3xB7wygJvIyA2V2uS+GsfPFysE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250131153610-6807614ac86b/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203105249-64bea2a66e35 h1:5Zkm2tPQ60l2oMdrf3/uC1mWOCU+ti77d0k9y/AW1z8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203105249-64bea2a66e35/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203113830-275bd56fe64c h1:4EW1OEHuRjH9B3LhQEvOLp3qPxnU4kDBwgKzy7KNlS4=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203113830-275bd56fe64c/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203124514-14977c7b2c39 h1:XW7Hny4W/2ClAZR2Wi9KRvLTH/pjmwpgXiwM+fDsy50=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203124514-14977c7b2c39/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203143322-22d15fe395e8 h1:OWBLh52Ee4Txs0PY4bMlfRbaTbfNNR/ndj2J+RGrR6k=
cloud.o-forge.io/core/oc-lib v0.0.0-20250203143322-22d15fe395e8/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204080055-bf114b39b7d5 h1:rsOMNER+ZIIt/as3bOU2lJe+MbCCR5x1iR/XyZYmuKU=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204080055-bf114b39b7d5/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204091410-2ccb57ffb050 h1:NdKJD+hbAyDaUfRkdtMUZLasR1d/BGyEfCvuozTso+Y=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204091410-2ccb57ffb050/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204110709-3061df4f13da h1:Mx3vR5r21H0zX+B0yaQOeOn3hvWJUrdy0DFLI+RAH1I=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204110709-3061df4f13da/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204134321-69bf9518661e h1:etAdc6jOnpm49RFs2Z8R7zzwfP/uGN6eQAmMGVqTEnc=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204134321-69bf9518661e/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204155113-a8e2445c103c h1:wNM/SweaGy+Wz4KV3+1wpLYgtDOSDK+WO6564TCGDjE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250204155113-a8e2445c103c/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205154116-7201cabb438a h1:DAEI00i+r2MAlUqqRJfW5FiXsWppQW8y51kKRl39WFA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205154116-7201cabb438a/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f h1:6V+Z81ywYoDYSVMnM4PVaJYXFgCN3xSG3ddiUPn4jL8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
@@ -95,6 +301,8 @@ github.com/beego/beego/v2 v2.3.2/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVD
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -385,6 +593,8 @@ github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQ
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/marcinwyszynski/geopoint v0.0.0-20140302213024-cf2a6f750c5b h1:XBF8THPBy28s2ryI7+/Jf/847unLWxYMpJveX5Kox+0=
github.com/marcinwyszynski/geopoint v0.0.0-20140302213024-cf2a6f750c5b/go.mod h1:z1oqhOuuYpPHmUmAK2aNygKFlPdb4o3PppQnVTRFdrI=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -466,6 +676,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2 h1:5w7Y/+01L0ErOp3qFiiAEpVlvzYJK2mjbLFcbBkx2OI= github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2 h1:5w7Y/+01L0ErOp3qFiiAEpVlvzYJK2mjbLFcbBkx2OI=
github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2/go.mod h1:/fUyI4rS5nHkKtgxNRU/uuFyhx9woSy3wKQSCQjqWN4= github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2/go.mod h1:/fUyI4rS5nHkKtgxNRU/uuFyhx9woSy3wKQSCQjqWN4=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=

View File

@@ -9,10 +9,10 @@ import (
type AuthConnector interface { type AuthConnector interface {
Status() tools.State Status() tools.State
Login(username string, cookies ...*http.Cookie) (*Token, error) Login(clientID string, username string, cookies ...*http.Cookie) (*Token, error)
Logout(token string, cookies ...*http.Cookie) (*Token, error) Logout(clientID string, token string, cookies ...*http.Cookie) (*Token, error)
Introspect(token string, cookie ...*http.Cookie) (bool, error) Introspect(token string, cookie ...*http.Cookie) (bool, error)
Refresh(token *Token) (*Token, error) Refresh(client_id string, 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

@@ -23,7 +23,6 @@ import (
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
@@ -85,7 +84,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(token *Token) (*Token, error) { func (a HydraConnector) Refresh(client_id string, 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], ".")
@@ -94,11 +93,11 @@ func (a HydraConnector) Refresh(token *Token) (*Token, error) {
if err != nil || !isValid { if err != nil || !isValid {
return nil, err return nil, err
} }
_, err = a.Logout(token.AccessToken) _, err = a.Logout(client_id, token.AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.Login(token.Username) return a.Login(client_id, 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) {
@@ -120,7 +119,7 @@ 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.Request.URL.String(), challenge, cookies...)
} }
func (a HydraConnector) getClient() string { func (a HydraConnector) getClient(clientID string) 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 ""
@@ -130,11 +129,17 @@ func (a HydraConnector) getClient() 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(username string, cookies ...*http.Cookie) (t *Token, err error) { func (a HydraConnector) Login(clientID string, username string, cookies ...*http.Cookie) (t *Token, err error) {
clientID := a.getClient() fmt.Println("login", clientID, username)
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...)
@@ -176,7 +181,7 @@ func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Toke
return nil, err return nil, err
} }
json.Unmarshal(b, &m) json.Unmarshal(b, &m)
pp := oclib.Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), oclib.LibDataEnum(oclib.PEER)) pp := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), false)
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")
} }
@@ -184,7 +189,8 @@ func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Toke
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(username, pp.Data[0].(*peer.Peer).Url) c := claims.GetClaims().AddClaimsToToken(clientID, 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)
@@ -194,7 +200,8 @@ func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Toke
return token, nil return token, nil
} }
func (a HydraConnector) Logout(token string, cookies ...*http.Cookie) (*Token, error) { func (a HydraConnector) Logout(clientID string, token string, cookies ...*http.Cookie) (*Token, error) {
clientID = a.getClient(clientID)
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], ".")
@@ -202,7 +209,7 @@ func (a HydraConnector) Logout(token string, cookies ...*http.Cookie) (*Token, e
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", a.getClient()) urls.Add("client_id", clientID)
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 {

View File

@@ -31,8 +31,9 @@ var (
type conn interface { type conn interface {
Bind(bindDN, password string) error Bind(bindDN, password string) error
SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) SearchRoles(attrs ...string) ([]map[string][]string, error)
SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) SearchUser(user string, attrs ...string) ([]map[string][]string, error)
SearchUserRoles(user string, attrs ...string) ([]map[string][]string, error)
Close() error Close() error
} }
@@ -78,7 +79,7 @@ type Client struct {
cache *freecache.Cache cache *freecache.Cache
} }
func (cli *Client) Authenticate(ctx context.Context, username, password string) (bool, error) { func (cli *Client) Authenticate(ctx context.Context, username string, password string) (bool, error) {
if username == "" || password == "" { if username == "" || password == "" {
return false, nil return false, nil
} }
@@ -101,8 +102,8 @@ func (cli *Client) Authenticate(ctx context.Context, username, password string)
if details == nil { if details == nil {
return false, nil return false, nil
} }
a := details["dn"]
if err := cn.Bind(details["dn"].(string), password); err != nil { if err := cn.Bind(a[0], password); err != nil {
if err == errInvalidCredentials { if err == errInvalidCredentials {
return false, nil return false, nil
} }
@@ -118,6 +119,21 @@ func (cli *Client) Authenticate(ctx context.Context, username, password string)
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
@@ -125,6 +141,10 @@ 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 == "" {
@@ -193,11 +213,12 @@ 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 {
roleDN, ok := entry["dn"].(string) roleDNs, ok := entry["dn"]
if !ok || roleDN == "" { if !ok || len(roleDNs) == 0 {
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
@@ -278,8 +299,79 @@ 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]interface{}, error) { func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string) (map[string][]string, 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 {
@@ -298,7 +390,7 @@ func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string
var ( var (
entry = entries[0] entry = entries[0]
details = make(map[string]interface{}) details = make(map[string][]string)
) )
for _, attr := range attrs { for _, attr := range attrs {
if v, ok := entry[attr]; ok { if v, ok := entry[attr]; ok {
@@ -349,35 +441,40 @@ func (c *ldapConn) Bind(bindDN, password string) error {
return err return err
} }
func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) { func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string][]string, 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]interface{}, error) { func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string][]string, error) {
query := fmt.Sprintf("(|"+ query := fmt.Sprintf("(|"+
"(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))"+ "(&(|(objectClass=group)(objectClass=groupOfNames)(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]interface{}, error) { func (c *ldapConn) searchEntries(baseDN, query string, attrs []string) ([]map[string][]string, 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]interface{} var entries []map[string][]string
for _, v := range res.Entries { for _, v := range res.Entries {
entry := map[string]interface{}{"dn": v.DN} entry := map[string][]string{"dn": []string{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[0] entry[attr.Name] = attr.Values
} }
entries = append(entries, entry) entries = append(entries, entry)
} }

View File

@@ -1,10 +1,14 @@
package claims package claims
import "oc-auth/conf" import (
"oc-auth/conf"
"cloud.o-forge.io/core/oc-lib/models/peer"
)
// Tokenizer interface // Tokenizer interface
type ClaimService interface { type ClaimService interface {
AddClaimsToToken(userId string, host string) Claims AddClaimsToToken(clientID string, 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,6 +4,7 @@ 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"
@@ -11,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"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"
) )
@@ -22,7 +24,7 @@ func (h HydraClaims) generateKey(relation string, path string) (string, error) {
return "", err return "", err
} }
p := strings.ReplaceAll(strings.ToUpper(path), "/", "_") p := strings.ReplaceAll(strings.ToUpper(path), "/", "_")
return strings.ToLower(method.String()) + "_" + strings.ReplaceAll(p, ":", ""), nil return strings.ToUpper(method.String()) + "_" + strings.ReplaceAll(p, ":", ""), nil
} }
// decode key expect to extract method and path from key // decode key expect to extract method and path from key
@@ -38,7 +40,7 @@ func (h HydraClaims) decodeKey(key string, external bool) (tools.METHOD, string,
if err != nil { if err != nil {
return meth, "", err return meth, "", err
} }
p := strings.ReplaceAll(strings.ToLower(s[1]), "_", "/") p := strings.ReplaceAll(strings.ToUpper(s[1]), "_", "/")
return meth, p, nil return meth, p, nil
} }
@@ -118,21 +120,23 @@ 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(userId string, host string) Claims { func (h HydraClaims) AddClaimsToToken(clientID string, 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 {
@@ -140,12 +144,19 @@ func (h HydraClaims) AddClaimsToToken(userId string, host string) Claims {
} }
claims.Session.AccessToken[key] = perm.Subject claims.Session.AccessToken[key] = perm.Subject
} }
sign, err := h.encodeSignature(host) sign, err := h.encodeSignature(p.Url)
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.IDToken["username"] = userId
claims.Session.IDToken["peer_id"] = p.UUID
// we should get group from user
groups, err := perms_connectors.KetoConnector{}.GetGroupByUser(userId)
if err != nil {
return claims
}
claims.Session.IDToken["client_id"] = clientID
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() perms_connectors.PermConnector { func GetPermissionConnector(client string) perms_connectors.PermConnector {
return perms_connectors.GetPermissionConnector() return perms_connectors.GetPermissionConnector(client)
} }
func GetClaims() claims.ClaimService { func GetClaims() claims.ClaimService {

View File

@@ -11,18 +11,24 @@ import (
"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" return "oc-auth-realm"
} }
func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string { func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string {
n := "?namespace=" + perm.Namespace() n := "?namespace=" + f.namespace()
if perm.Object != "" { if perm.Object != "" {
n += "&object=" + perm.Object n += "&object=" + perm.Object
} }
@@ -78,13 +84,21 @@ func (k KetoConnector) CheckPermission(perm Permission, permDependancies *Permis
return len(perms) > 0 return len(perms) > 0
} }
func (k KetoConnector) DeleteRole(roleID string) (string, int, error) { func (k KetoConnector) deletes(object string, relation string, subject string, relation2 string) (string, int, error) {
k.deleteRelationShip("", "", roleID, nil) k.deleteRelationShip(object, relation, subject, nil)
_, code, err := k.deleteRelationShip(roleID, "", k.scope(), nil) _, code, err := k.deleteRelationShip(subject, relation2, k.scope(), nil)
if err != nil { if err != nil {
return "", code, err return "", code, err
} }
return roleID, 200, nil return subject, 200, nil
}
func (k KetoConnector) DeleteRole(roleID string) (string, int, error) {
return k.deletes("", "member", roleID, "is")
}
func (k KetoConnector) DeleteGroup(groupID string) (string, int, error) {
return k.deletes("", "groups", groupID, "groupin")
} }
func (k KetoConnector) DeletePermission(permID string, relation string, internal bool) (string, int, error) { func (k KetoConnector) DeletePermission(permID string, relation string, internal bool) (string, int, error) {
@@ -95,20 +109,15 @@ func (k KetoConnector) DeletePermission(permID string, relation string, internal
} }
return "", 200, err return "", 200, err
} }
k.deleteRelationShip("", "", permID, nil) return k.deletes("", "groups", permID, "permits"+meth.String())
_, code, err := k.deleteRelationShip(permID, "permits"+meth.String(), k.scope(), nil)
if err != nil {
return "", code, err
}
return permID, 200, nil
} }
func (k KetoConnector) CreateRole(roleID string) (string, int, error) { func (k KetoConnector) CreateRole(roleID string) (string, int, error) {
p, code, err := k.createRelationShip(roleID, "is", k.scope(), nil) return k.creates(roleID, "is", k.scope())
if err != nil { }
return "", code, err
} func (k KetoConnector) CreateGroup(groupID string) (string, int, error) {
return p.Object, 200, nil return k.creates(groupID, "groupin", k.scope())
} }
func (k KetoConnector) CreatePermission(permID string, relation string, internal bool) (string, int, error) { func (k KetoConnector) CreatePermission(permID string, relation string, internal bool) (string, int, error) {
@@ -116,9 +125,12 @@ func (k KetoConnector) CreatePermission(permID string, relation string, internal
if err != nil { if err != nil {
return "", 422, err return "", 422, err
} }
k.BindPermission("admin", permID, "permits"+meth.String()) k.BindPermission("admin", permID, "permits"+meth.String())
p, code, err := k.createRelationShip(permID, "permits"+meth.String(), k.scope(), nil) return k.creates(permID, "permits"+meth.String(), k.scope())
}
func (k KetoConnector) creates(object string, relation string, subject string) (string, int, error) {
p, code, err := k.createRelationShip(object, relation, subject, nil)
if err != nil { if err != nil {
return "", code, err return "", code, err
} }
@@ -126,25 +138,29 @@ func (k KetoConnector) CreatePermission(permID string, relation string, internal
} }
func (k KetoConnector) GetRole(roleID string) ([]string, error) { func (k KetoConnector) GetRole(roleID string) ([]string, error) {
arr := []string{} return k.gets(roleID, "is", k.scope())
roles, err := k.get(roleID, "is", k.scope()) }
if err != nil {
return arr, err func (k KetoConnector) GetGroup(groupID string) ([]string, error) {
} return k.gets(groupID, "groupin", k.scope())
for _, role := range roles {
arr = append(arr, role.Object)
}
return arr, nil
} }
func (k KetoConnector) GetRoleByUser(userID string) ([]string, error) { func (k KetoConnector) GetRoleByUser(userID string) ([]string, error) {
return k.gets("", "member", userID)
}
func (k KetoConnector) GetGroupByUser(userID string) ([]string, error) {
return k.gets("", "groups", userID)
}
func (k KetoConnector) gets(object string, relation string, subject string) ([]string, error) {
arr := []string{} arr := []string{}
roles, err := k.get("", "member", userID) objs, err := k.get(object, relation, subject)
if err != nil { if err != nil {
return arr, err return arr, err
} }
for _, role := range roles { for _, obj := range objs {
arr = append(arr, role.Object) arr = append(arr, obj.Object)
} }
return arr, nil return arr, nil
} }
@@ -178,6 +194,7 @@ 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
} }
@@ -224,40 +241,63 @@ func (k KetoConnector) get(object string, relation string, subject string) ([]Pe
return t, nil return t, nil
} }
func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) { func (k KetoConnector) binds(object string, relation string, subject string) (string, int, error) {
_, code, err := k.createRelationShip(roleID, "member", userID, nil) _, code, err := k.createRelationShip(object, relation, subject, nil)
if err != nil { if err != nil {
return roleID, code, err return object, code, err
} }
return roleID, 200, nil return object, 200, nil
}
func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) {
fmt.Println("BIND ROLE", userID, roleID)
return k.binds(userID, "member", roleID)
}
func (k KetoConnector) BindGroup(userID string, groupID string) (string, int, error) {
return k.binds(userID, "groups", groupID)
} }
func (k KetoConnector) BindPermission(roleID string, permID string, relation string) (*Permission, int, error) { func (k KetoConnector) BindPermission(roleID string, permID string, relation string) (*Permission, int, error) {
perms, err := k.GetPermission(permID, relation) perms, err := k.GetPermission(permID, relation)
if err != nil || len(perms) != 1 { if err != nil || len(perms) != 1 {
if len(perms) == 0 { count := 0
for _, p := range perms {
if p.Relation == relation {
count++
}
}
if count == 0 {
return nil, 404, errors.New("Permission not found") return nil, 404, errors.New("Permission not found")
} else if len(perms) > 1 { } else if count > 1 {
return nil, 409, errors.New("Multiple permission found") return nil, 409, errors.New("Multiple permission found")
} }
} }
_, code, err := k.createRelationShip(roleID, perms[0].Relation, permID, nil) _, code, err := k.createRelationShip(roleID, relation, permID, nil)
if err != nil { if err != nil {
return nil, code, err return nil, code, err
} }
return &Permission{ return &Permission{
Object: roleID, Object: roleID,
Relation: perms[0].Relation, Relation: relation,
Subject: permID, Subject: permID,
}, 200, nil }, 200, nil
} }
func (k KetoConnector) UnBindRole(userID string, roleID string) (string, int, error) { func (k KetoConnector) unbinds(subject string, relation string, object string) (string, int, error) {
_, code, err := k.deleteRelationShip(roleID, "member", userID, nil) _, code, err := k.deleteRelationShip(object, relation, subject, nil)
if err != nil { if err != nil {
return roleID, code, err return object, code, err
} }
return roleID, 200, nil return object, 200, nil
}
func (k KetoConnector) UnBindRole(userID string, roleID string) (string, int, error) {
return k.unbinds(userID, "member", roleID)
}
func (k KetoConnector) UnBindGroup(userID string, groupID string) (string, int, error) {
return k.unbinds(userID, "groups", groupID)
} }
func (k KetoConnector) UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error) { func (k KetoConnector) UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error) {
@@ -267,9 +307,15 @@ func (k KetoConnector) UnBindPermission(roleID string, permID string, relation s
} }
perms, err := k.GetPermission(permID, meth.String()) perms, err := k.GetPermission(permID, meth.String())
if err != nil || len(perms) != 1 { if err != nil || len(perms) != 1 {
if len(perms) == 0 { count := 0
for _, p := range perms {
if p.Relation == relation {
count++
}
}
if count == 0 {
return nil, 404, errors.New("Permission not found") return nil, 404, errors.New("Permission not found")
} else if len(perms) > 1 { } else if count > 1 {
return nil, 409, errors.New("Multiple permission found") return nil, 409, errors.New("Multiple permission found")
} }
} }
@@ -296,7 +342,7 @@ 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": s.Namespace(), "object": s.Object, "relation": s.Relation, "subject_id": s.Subject} body["subject_set"] = map[string]interface{}{"namespace": k.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)

View File

@@ -23,23 +23,30 @@ 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)
BindPermission(roleID string, permID string, relation string) (*Permission, int, error) BindPermission(roleID string, permID string, relation string) (*Permission, int, error)
UnBindRole(userID string, roleID string) (string, int, error) UnBindRole(userID string, roleID string) (string, int, error)
UnBindGroup(userID string, groupID string) (string, int, error)
UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error) UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error)
CreateRole(roleID string) (string, int, error) CreateRole(roleID string) (string, int, error)
CreateGroup(groupID string) (string, int, error)
CreatePermission(permID string, relation string, internal bool) (string, int, error) CreatePermission(permID string, relation string, internal bool) (string, int, error)
DeleteRole(roleID string) (string, int, error) DeleteRole(roleID string) (string, int, error)
DeleteGroup(groupID string) (string, int, error)
DeletePermission(permID string, relation string, internal bool) (string, int, error) DeletePermission(permID string, relation string, internal bool) (string, int, error)
GetRoleByUser(userID string) ([]string, error) GetRoleByUser(userID string) ([]string, error)
GetGroupByUser(userID string) ([]string, error)
GetPermissionByRole(roleID string) ([]Permission, error) GetPermissionByRole(roleID string) ([]Permission, error)
GetPermissionByUser(userID string, internal bool) ([]Permission, error) GetPermissionByUser(userID string, internal bool) ([]Permission, error)
GetRole(roleID string) ([]string, error) GetRole(roleID string) ([]string, error)
GetGroup(groupID string) ([]string, error)
GetPermission(permID string, relation string) ([]Permission, error) GetPermission(permID string, relation string) ([]Permission, error)
} }
@@ -47,6 +54,6 @@ var c = map[string]PermConnector{
"keto": KetoConnector{}, "keto": KetoConnector{},
} }
func GetPermissionConnector() PermConnector { func GetPermissionConnector(scope string) PermConnector {
return c[conf.GetConfig().PermissionConnectorHost] return c[conf.GetConfig().PermissionConnectorHost]
} }

View File

@@ -0,0 +1,78 @@
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

24
ldap-hydra/ldap-2.ldif Normal file
View File

@@ -0,0 +1,24 @@
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

46
main.go
View File

@@ -1,10 +1,12 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt" "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"
@@ -43,6 +45,7 @@ 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")
@@ -52,10 +55,36 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
generateRole()
discovery() discovery()
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
@@ -66,15 +95,17 @@ 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.Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), oclib.LibDataEnum(oclib.PEER)) p := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).Search(nil, strconv.Itoa(peer.SELF.EnumIndex()), false)
if len(p.Data) > 0 { file := ""
// check public key with the one in the database
f, err := os.ReadFile(conf.GetConfig().PublicKeyPath) f, err := os.ReadFile(conf.GetConfig().PublicKeyPath)
if err != nil { if err != nil {
return err return err
} }
file = string(f)
if len(p.Data) > 0 {
// check public key with the one in the database
// compare the public key from file with the one in the database // compare the public key from file with the one in the database
if !strings.Contains(string(f), p.Data[0].(*peer.Peer).PublicKey) { if !strings.Contains(file, p.Data[0].(*peer.Peer).PublicKey) {
return errors.New("public key is different from the one in the database") return errors.New("public key is different from the one in the database")
} }
return nil return nil
@@ -86,10 +117,10 @@ func generateSelfPeer() error {
AbstractObject: utils.AbstractObject{ AbstractObject: utils.AbstractObject{
Name: o.GetStringDefault("NAME", "local"), Name: o.GetStringDefault("NAME", "local"),
}, },
PublicKey: conf.GetConfig().PublicKeyPath, PublicKey: file,
State: peer.SELF, State: peer.SELF,
} }
data := oclib.StoreOne(oclib.LibDataEnum(oclib.PEER), peer.Serialize()) data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).StoreOne(peer.Serialize(peer))
if data.Err != "" { if data.Err != "" {
return errors.New(data.Err) return errors.New(data.Err)
} }
@@ -97,9 +128,8 @@ func generateSelfPeer() error {
} }
func discovery() { func discovery() {
fmt.Println("Discovered")
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

@@ -7,9 +7,81 @@ import (
func init() { func init() {
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "GetAll",
Router: `/`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Post",
Router: `/:id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Get",
Router: `/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Delete",
Router: `/:id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Bind",
Router: `/:user_id/:group_id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "UnBind",
Router: `/:user_id/:group_id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Clear",
Router: `/clear`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "GetByUser",
Router: `/user/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
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: "InternalAuthForward", Method: "InternaisDraftlAuthForward",
Router: `/forward`, Router: `/forward`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
@@ -27,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: "LoginLDAP", Method: "Login",
Router: `/ldap/login`, Router: `/login`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
@@ -36,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: "LogOutLDAP", Method: "LogOut",
Router: `/ldap/logout`, Router: `/logout`,
AllowHTTPMethods: []string{"delete"}, AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,

View File

@@ -18,6 +18,11 @@ func init() {
beego.NSInclude( beego.NSInclude(
&controllers.OAuthController{}, &controllers.OAuthController{},
), ),
beego.NSNamespace("/group",
beego.NSInclude(
&controllers.GroupController{},
),
),
beego.NSNamespace("/role", beego.NSNamespace("/role",
beego.NSInclude( beego.NSInclude(
&controllers.RoleController{}, &controllers.RoleController{},

View File

@@ -37,6 +37,180 @@
} }
} }
}, },
"/group/": {
"get": {
"tags": [
"group"
],
"description": "find groups\n\u003cbr\u003e",
"operationId": "GroupController.GetAll",
"responses": {
"200": {
"description": "{group} string"
}
}
}
},
"/group/clear": {
"delete": {
"tags": [
"group"
],
"description": "clear the group\n\u003cbr\u003e",
"operationId": "GroupController.Clear",
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/group/user/{id}": {
"get": {
"tags": [
"group"
],
"description": "find group by user id\n\u003cbr\u003e",
"operationId": "GroupController.GetByUser",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} string"
}
}
}
},
"/group/{id}": {
"get": {
"tags": [
"group"
],
"description": "find group by id\n\u003cbr\u003e",
"operationId": "GroupController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{group} string"
}
}
},
"post": {
"tags": [
"group"
],
"description": "create group\n\u003cbr\u003e",
"operationId": "GroupController.Create",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} create success!"
}
}
},
"delete": {
"tags": [
"group"
],
"description": "delete the group\n\u003cbr\u003e",
"operationId": "GroupController.Delete",
"parameters": [
{
"in": "path",
"name": "id",
"description": "The id you want to delete",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/group/{user_id}/{group_id}": {
"post": {
"tags": [
"group"
],
"description": "bind the group to user\n\u003cbr\u003e",
"operationId": "GroupController.Bind",
"parameters": [
{
"in": "path",
"name": "user_id",
"description": "The user_id you want to bind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "group_id",
"description": "The group_id you want to bind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
},
"delete": {
"tags": [
"group"
],
"description": "unbind the group to user\n\u003cbr\u003e",
"operationId": "GroupController.UnBind",
"parameters": [
{
"in": "path",
"name": "user_id",
"description": "The group_id you want to unbind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "group_id",
"description": "The user_id you want to unbind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
}
},
"/introspect": { "/introspect": {
"get": { "get": {
"tags": [ "tags": [
@@ -59,7 +233,7 @@
} }
} }
}, },
"/ldap/login": { "/login": {
"post": { "post": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@@ -75,6 +249,13 @@
"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": {
@@ -84,7 +265,7 @@
} }
} }
}, },
"/ldap/logout": { "/logout": {
"delete": { "delete": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@@ -97,6 +278,13 @@
"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": {
@@ -291,6 +479,13 @@
"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": {
@@ -518,6 +713,10 @@
"name": "oc-auth/controllersOAuthController", "name": "oc-auth/controllersOAuthController",
"description": "Operations about auth\n" "description": "Operations about auth\n"
}, },
{
"name": "group",
"description": "Operations about auth\n"
},
{ {
"name": "role", "name": "role",
"description": "Operations about auth\n" "description": "Operations about auth\n"

View File

@@ -28,6 +28,137 @@ paths:
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
/group/:
get:
tags:
- group
description: |-
find groups
<br>
operationId: GroupController.GetAll
responses:
"200":
description: '{group} string'
/group/{id}:
get:
tags:
- group
description: |-
find group by id
<br>
operationId: GroupController.Get
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{group} string'
post:
tags:
- group
description: |-
create group
<br>
operationId: GroupController.Create
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} create success!'
delete:
tags:
- group
description: |-
delete the group
<br>
operationId: GroupController.Delete
parameters:
- in: path
name: id
description: The id you want to delete
required: true
type: string
responses:
"200":
description: '{string} delete success!'
/group/{user_id}/{group_id}:
post:
tags:
- group
description: |-
bind the group to user
<br>
operationId: GroupController.Bind
parameters:
- in: path
name: user_id
description: The user_id you want to bind
required: true
type: string
- in: path
name: group_id
description: The group_id you want to bind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
delete:
tags:
- group
description: |-
unbind the group to user
<br>
operationId: GroupController.UnBind
parameters:
- in: path
name: user_id
description: The group_id you want to unbind
required: true
type: string
- in: path
name: group_id
description: The user_id you want to unbind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
/group/clear:
delete:
tags:
- group
description: |-
clear the group
<br>
operationId: GroupController.Clear
responses:
"200":
description: '{string} delete success!'
/group/user/{id}:
get:
tags:
- group
description: |-
find group by user id
<br>
operationId: GroupController.GetByUser
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} string'
/introspect: /introspect:
get: get:
tags: tags:
@@ -44,7 +175,7 @@ paths:
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
/ldap/login: /login:
post: post:
tags: tags:
- oc-auth/controllersOAuthController - oc-auth/controllersOAuthController
@@ -59,10 +190,15 @@ 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}'
/ldap/logout: /logout:
delete: delete:
tags: tags:
- oc-auth/controllersOAuthController - oc-auth/controllersOAuthController
@@ -75,6 +211,11 @@ 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}'
@@ -219,6 +360,11 @@ 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}'
@@ -386,6 +532,9 @@ tags:
- name: oc-auth/controllersOAuthController - name: oc-auth/controllersOAuthController
description: | description: |
Operations about auth Operations about auth
- name: group
description: |
Operations about auth
- name: role - name: role
description: | description: |
Operations about auth Operations about auth