diff --git a/conf/config.go b/conf/config.go index 4464958..d3f5285 100644 --- a/conf/config.go +++ b/conf/config.go @@ -3,6 +3,7 @@ package conf import "sync" type Config struct { + SourceMode string AdminRole string PublicKeyPath string PrivateKeyPath string diff --git a/controllers/group.go b/controllers/group.go index d41040f..08a4947 100644 --- a/controllers/group.go +++ b/controllers/group.go @@ -19,7 +19,8 @@ type GroupController struct { func (o *GroupController) Post() { // store and return Id or post with UUID id := o.Ctx.Input.Param(":id") - group, code, err := infrastructure.GetPermissionConnector().CreateGroup(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, @@ -44,7 +45,8 @@ func (o *GroupController) Post() { // @router /user/:id [get] func (o *GroupController) GetByUser() { id := o.Ctx.Input.Param(":id") - group, err := infrastructure.GetPermissionConnector().GetGroupByUser(id) + clientID := ExtractClient(*o.Ctx.Request) + group, err := infrastructure.GetPermissionConnector(clientID).GetGroupByUser(id) if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -67,7 +69,8 @@ func (o *GroupController) GetByUser() { // @Success 200 {group} string // @router / [get] func (o *GroupController) GetAll() { - group, err := infrastructure.GetPermissionConnector().GetGroup("") + clientID := ExtractClient(*o.Ctx.Request) + group, err := infrastructure.GetPermissionConnector(clientID).GetGroup("") if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -92,7 +95,8 @@ func (o *GroupController) GetAll() { // @router /:id [get] func (o *GroupController) Get() { id := o.Ctx.Input.Param(":id") - group, err := infrastructure.GetPermissionConnector().GetGroup(id) + clientID := ExtractClient(*o.Ctx.Request) + group, err := infrastructure.GetPermissionConnector(clientID).GetGroup(id) if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -117,7 +121,8 @@ func (o *GroupController) Get() { // @router /:id [delete] func (o *GroupController) Delete() { id := o.Ctx.Input.Param(":id") - group, code, err := infrastructure.GetPermissionConnector().DeleteGroup(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, @@ -140,7 +145,8 @@ func (o *GroupController) Delete() { // @Success 200 {string} delete success! // @router /clear [delete] func (o *GroupController) Clear() { - group, code, err := infrastructure.GetPermissionConnector().DeleteGroup("") + clientID := ExtractClient(*o.Ctx.Request) + group, code, err := infrastructure.GetPermissionConnector(clientID).DeleteGroup("") if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -167,7 +173,8 @@ func (o *GroupController) Clear() { func (o *GroupController) Bind() { user_id := o.Ctx.Input.Param(":user_id") group_id := o.Ctx.Input.Param(":group_id") - group, code, err := infrastructure.GetPermissionConnector().BindGroup(user_id, 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, @@ -187,14 +194,15 @@ func (o *GroupController) Bind() { // @Title UnBind // @Description unbind the group to user -// @Param group_id path string true "The group_id you want to unbind" +// @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") - group, code, err := infrastructure.GetPermissionConnector().UnBindGroup(user_id, 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, diff --git a/controllers/oauth2.go b/controllers/oauth2.go index ea90d8a..1acc7fb 100644 --- a/controllers/oauth2.go +++ b/controllers/oauth2.go @@ -1,9 +1,11 @@ package controllers import ( + "encoding/base64" "encoding/json" "fmt" "net/http" + "oc-auth/conf" "oc-auth/infrastructure" auth_connectors "oc-auth/infrastructure/auth_connector" "regexp" @@ -22,10 +24,12 @@ type OAuthController struct { // @Title Logout // @Description unauthenticate user // @Param Authorization header string false "auth token" +// @Param client_id query string true "the client_id you want to get" // @Success 200 {string} -// @router /ldap/logout [delete] -func (o *OAuthController) LogOutLDAP() { +// @router /logout [delete] +func (o *OAuthController) LogOut() { // authorize user + clientID := o.Ctx.Input.Query("client_id") reqToken := o.Ctx.Request.Header.Get("Authorization") splitToken := strings.Split(reqToken, "Bearer ") if len(splitToken) < 2 { @@ -36,7 +40,7 @@ func (o *OAuthController) LogOutLDAP() { var res auth_connectors.Token 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 { o.Data["json"] = err } else { @@ -48,25 +52,33 @@ func (o *OAuthController) LogOutLDAP() { // @Title Login // @Description authenticate user // @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} -// @router /ldap/login [post] -func (o *OAuthController) LoginLDAP() { +// @router /login [post] +func (o *OAuthController) Login() { // 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 json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res) - ldap := auth_connectors.New() - found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password) - if err != nil || !found { - o.Data["json"] = err - o.Ctx.ResponseWriter.WriteHeader(401) - o.ServeJSON() - return + if conf.GetConfig().SourceMode == "ldap" { + ldap := auth_connectors.New() + found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password) + fmt.Println("found", found, "err", err) + if err != nil || !found { + o.Data["json"] = err + o.Ctx.ResponseWriter.WriteHeader(401) + o.ServeJSON() + return + } } - token, err := infrastructure.GetAuthConnector().Login(res.Username, + token, err := infrastructure.GetAuthConnector().Login( + clientID, res.Username, &http.Cookie{ // open a session Name: "csrf_token", Value: o.XSRFToken(), }) + fmt.Println("token", token, "err", err) if err != nil || token == nil { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) @@ -79,13 +91,15 @@ func (o *OAuthController) LoginLDAP() { // @Title Introspection // @Description introspect token // @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} // @router /refresh [post] func (o *OAuthController) Refresh() { + clientID := o.Ctx.Input.Query("client_id") var token auth_connectors.Token json.Unmarshal(o.Ctx.Input.CopyBody(100000), &token) // refresh token - newToken, err := infrastructure.GetAuthConnector().Refresh(&token) + newToken, err := infrastructure.GetAuthConnector().Refresh(clientID, &token) if err != nil || newToken == nil { o.Data["json"] = err o.Ctx.ResponseWriter.WriteHeader(401) @@ -128,7 +142,7 @@ var whitelist = []string{ // @Param Authorization header string false "auth token" // @Success 200 {string} // @router /forward [get] -func (o *OAuthController) InternalAuthForward() { +func (o *OAuthController) InternaisDraftlAuthForward() { fmt.Println("InternalAuthForward") reqToken := o.Ctx.Request.Header.Get("Authorization") if reqToken == "" { @@ -149,7 +163,7 @@ func (o *OAuthController) InternalAuthForward() { } else { reqToken = splitToken[1] } - origin, publicKey, external := o.extractOrigin() + origin, publicKey, external := o.extractOrigin(o.Ctx.Request) if !infrastructure.GetAuthConnector().CheckAuthForward( //reqToken != "" && reqToken, publicKey, origin, o.Ctx.Request.Header.Get("X-Forwarded-Method"), @@ -161,7 +175,8 @@ func (o *OAuthController) InternalAuthForward() { 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 publicKey := "" origin := o.Ctx.Request.Header.Get("X-Forwarded-Host") @@ -174,7 +189,7 @@ func (o *OAuthController) extractOrigin() (string, string, bool) { if t != "" { 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 return "", "", external } @@ -190,3 +205,29 @@ func (o *OAuthController) extractOrigin() (string, string, bool) { } 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 "" +} diff --git a/controllers/permission.go b/controllers/permission.go index a51d763..678f2bb 100644 --- a/controllers/permission.go +++ b/controllers/permission.go @@ -16,7 +16,8 @@ type PermissionController struct { // @Success 200 {permission} string // @router / [get] func (o *PermissionController) GetAll() { - role, err := infrastructure.GetPermissionConnector().GetPermission("", "") + clientID := ExtractClient(*o.Ctx.Request) + role, err := infrastructure.GetPermissionConnector(clientID).GetPermission("", "") if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -41,7 +42,8 @@ func (o *PermissionController) GetAll() { // @router /role/:id [get] func (o *PermissionController) GetByRole() { 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -66,7 +68,8 @@ func (o *PermissionController) GetByRole() { // @router /user/:id [get] func (o *PermissionController) GetByUser() { 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -92,7 +95,8 @@ func (o *PermissionController) GetByUser() { func (o *PermissionController) Get() { id := o.Ctx.Input.Param(":id") 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -115,7 +119,8 @@ func (o *PermissionController) Get() { // @Success 200 {string} delete success! // @router /clear [delete] 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -144,7 +149,8 @@ func (o *PermissionController) Bind() { permission_id := o.Ctx.Input.Param(":permission_id") role_id := o.Ctx.Input.Param(":role_id") 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -173,7 +179,8 @@ func (o *PermissionController) UnBind() { permission_id := o.Ctx.Input.Param(":permission_id") role_id := o.Ctx.Input.Param(":role_id") 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 { o.Data["json"] = map[string]interface{}{ "data": nil, diff --git a/controllers/role.go b/controllers/role.go index 206ff1d..bcabdc0 100644 --- a/controllers/role.go +++ b/controllers/role.go @@ -19,7 +19,8 @@ type RoleController struct { func (o *RoleController) Post() { // store and return Id or post with UUID 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -44,7 +45,8 @@ func (o *RoleController) Post() { // @router /user/:id [get] func (o *RoleController) GetByUser() { 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -67,7 +69,8 @@ func (o *RoleController) GetByUser() { // @Success 200 {role} string // @router / [get] func (o *RoleController) GetAll() { - role, err := infrastructure.GetPermissionConnector().GetRole("") + clientID := ExtractClient(*o.Ctx.Request) + role, err := infrastructure.GetPermissionConnector(clientID).GetRole("") if err != nil { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -92,7 +95,8 @@ func (o *RoleController) GetAll() { // @router /:id [get] func (o *RoleController) Get() { 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -117,7 +121,8 @@ func (o *RoleController) Get() { // @router /:id [delete] func (o *RoleController) Delete() { 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -140,7 +145,8 @@ func (o *RoleController) Delete() { // @Success 200 {string} delete success! // @router /clear [delete] 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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -167,7 +173,8 @@ func (o *RoleController) Clear() { func (o *RoleController) Bind() { user_id := o.Ctx.Input.Param(":user_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 { o.Data["json"] = map[string]interface{}{ "data": nil, @@ -194,7 +201,8 @@ func (o *RoleController) Bind() { func (o *RoleController) UnBind() { user_id := o.Ctx.Input.Param(":user_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 { o.Data["json"] = map[string]interface{}{ "data": nil, diff --git a/docker-compose-2.yml b/docker-compose-2.yml new file mode 100644 index 0000000..c72e378 --- /dev/null +++ b/docker-compose-2.yml @@ -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 \ No newline at end of file diff --git a/go.mod b/go.mod index b74cccf..e6a0882 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 toolchain go1.23.3 require ( - cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be + cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f github.com/beego/beego/v2 v2.3.1 github.com/smartystreets/goconvey v1.7.2 go.uber.org/zap v1.27.0 @@ -17,6 +17,7 @@ require ( require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/biter777/countries v1.7.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -38,7 +39,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nats-io/nats.go v1.37.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/robfig/cron v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.10.0 // indirect diff --git a/go.sum b/go.sum index ac38025..d089dfe 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be h1:1Yf8ihUxXjOEPqcfgtXJpJ/slxBUHhf7AgS7DZI3iUk= -cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo= +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= 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -9,6 +9,8 @@ github.com/beego/beego/v2 v2.3.1 h1:7MUKMpJYzOXtCUsTEoXOxsDV/UcHw6CPbaWMlthVNsc= github.com/beego/beego/v2 v2.3.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4= 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/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q= +github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -175,8 +177,8 @@ github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPA github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -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 v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= diff --git a/infrastructure/auth_connector/auth_connector.go b/infrastructure/auth_connector/auth_connector.go index 9032ddf..39a23ab 100644 --- a/infrastructure/auth_connector/auth_connector.go +++ b/infrastructure/auth_connector/auth_connector.go @@ -9,10 +9,10 @@ import ( type AuthConnector interface { Status() tools.State - Login(username string, cookies ...*http.Cookie) (*Token, error) - Logout(token string, cookies ...*http.Cookie) (*Token, error) + Login(clientID string, username string, cookies ...*http.Cookie) (*Token, error) + Logout(clientID string, token string, cookies ...*http.Cookie) (*Token, 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 } diff --git a/infrastructure/auth_connector/hydra_connector.go b/infrastructure/auth_connector/hydra_connector.go index 9b1833d..3c8b509 100644 --- a/infrastructure/auth_connector/hydra_connector.go +++ b/infrastructure/auth_connector/hydra_connector.go @@ -30,7 +30,6 @@ import ( type HydraConnector struct { State string `json:"state"` Scopes string `json:"scope"` - ClientID string `json:"client_id"` ResponseType string `json:"response_type"` Caller *tools.HTTPCaller @@ -92,7 +91,7 @@ func (a HydraConnector) challenge(username string, url string, challenge string, 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, ".") if len(access) > 2 { token.AccessToken = strings.Join(access[0:2], ".") @@ -101,11 +100,11 @@ func (a HydraConnector) Refresh(token *Token) (*Token, error) { if err != nil || !isValid { return nil, err } - _, err = a.Logout(token.AccessToken) + _, err = a.Logout(client_id, token.AccessToken) if err != nil { 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) { @@ -141,7 +140,7 @@ func (a HydraConnector) tryLog(username string, url string, subpath string, chal return a.challenge(username, resp.Header.Get("Location"), challenge, cookies...) } -func (a HydraConnector) getClient() string { +func (a HydraConnector) getClient(clientID string) string { resp, err := a.Caller.CallGet(a.getPath(true, false), "/clients") if err != nil { return "" @@ -151,11 +150,17 @@ func (a HydraConnector) getClient() string { if err != nil || len(clients) == 0 { 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) } -func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Token, err error) { - clientID := a.getClient() +func (a HydraConnector) Login(clientID string, username string, cookies ...*http.Cookie) (t *Token, err error) { + fmt.Println("login", clientID, username) + clientID = a.getClient(clientID) 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, "login", cookies...) @@ -220,7 +225,7 @@ func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Toke return nil, err } 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 != "" { return nil, errors.New("peer not found") } @@ -228,7 +233,8 @@ func (a HydraConnector) Login(username string, cookies ...*http.Cookie) (t *Toke now = now.Add(time.Duration(token.ExpiresIn) * time.Second) unix := now.Unix() - c := claims.GetClaims().AddClaimsToToken(username, pp.Data[0].(*peer.Peer)) + c := claims.GetClaims().AddClaimsToToken(clientID, username, pp.Data[0].(*peer.Peer)) + fmt.Println("claims", c.Session.AccessToken) c.Session.AccessToken["exp"] = unix b, _ = json.Marshal(c) @@ -286,7 +292,8 @@ func (a HydraConnector) getClientset() (*kubernetes.Clientset, error) { return kubernetes.NewForConfig(config) } -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, ".") if len(access) > 2 { token = strings.Join(access[0:2], ".") @@ -294,7 +301,7 @@ func (a HydraConnector) Logout(token string, cookies ...*http.Cookie) (*Token, e p := a.getPath(false, true) + "/revoke" urls := url.Values{} urls.Add("token", token) - urls.Add("client_id", a.getClient()) + urls.Add("client_id", clientID) urls.Add("client_secret", conf.GetConfig().ClientSecret) _, err := a.Caller.CallForm(http.MethodPost, p, "", urls, "application/x-www-form-urlencoded", true) if err != nil { diff --git a/infrastructure/auth_connector/ldap.go b/infrastructure/auth_connector/ldap.go index aa3e823..0d0c57d 100644 --- a/infrastructure/auth_connector/ldap.go +++ b/infrastructure/auth_connector/ldap.go @@ -31,8 +31,9 @@ var ( type conn interface { Bind(bindDN, password string) error - SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) - SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) + SearchRoles(attrs ...string) ([]map[string][]string, error) + SearchUser(user string, attrs ...string) ([]map[string][]string, error) + SearchUserRoles(user string, attrs ...string) ([]map[string][]string, error) Close() error } @@ -78,7 +79,7 @@ type Client struct { 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 == "" { return false, nil } @@ -101,8 +102,8 @@ func (cli *Client) Authenticate(ctx context.Context, username, password string) if details == nil { return false, nil } - - if err := cn.Bind(details["dn"].(string), password); err != nil { + a := details["dn"] + if err := cn.Bind(a[0], password); err != nil { if err == errInvalidCredentials { return false, nil } @@ -118,6 +119,21 @@ func (cli *Client) Authenticate(ctx context.Context, username, password string) 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 type LDAPClaim struct { Code string // the root claim name @@ -125,6 +141,10 @@ type LDAPClaim struct { Value interface{} // the value } +type LDAPRoles struct { + Members map[string][]string +} + // FindOIDCClaims finds all OIDC claims for a user. func (cli *Client) FindOIDCClaims(ctx context.Context, username string) ([]LDAPClaim, error) { if username == "" { @@ -193,11 +213,12 @@ func (cli *Client) FindOIDCClaims(ctx context.Context, username string) ([]LDAPC roles := make(map[string]interface{}) for _, entry := range entries { - roleDN, ok := entry["dn"].(string) - if !ok || roleDN == "" { + roleDNs, ok := entry["dn"] + if !ok || len(roleDNs) == 0 { log.Infow("No required LDAP attribute for a role", "ldapAttribute", "dn", "entry", entry) continue } + roleDN := roleDNs[0] if entry[cli.RoleAttr] == nil { log.Infow("No required LDAP attribute for a role", "ldapAttribute", cli.RoleAttr, "roleDN", roleDN) continue @@ -278,8 +299,79 @@ func (cli *Client) connect(ctx context.Context) <-chan conn { 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. -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 != "" { // 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 { @@ -298,7 +390,7 @@ func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string var ( entry = entries[0] - details = make(map[string]interface{}) + details = make(map[string][]string) ) for _, attr := range attrs { if v, ok := entry[attr]; ok { @@ -349,35 +441,40 @@ func (c *ldapConn) Bind(bindDN, password string) error { 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( "(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))"+ "(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))", user) 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("(|"+ - "(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))"+ + "(&(|(objectClass=group)(objectClass=groupOfNames)(objectClass=groupofnames))(member=%[1]s))"+ "(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s))"+ ")", user) 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. -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) res, err := c.Search(req) if err != nil { return nil, err } - var entries []map[string]interface{} + var entries []map[string][]string 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 { // 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) } diff --git a/infrastructure/claims/claims.go b/infrastructure/claims/claims.go index f326b07..7281a65 100644 --- a/infrastructure/claims/claims.go +++ b/infrastructure/claims/claims.go @@ -8,7 +8,7 @@ import ( // Tokenizer interface type ClaimService interface { - AddClaimsToToken(userId string, peer *peer.Peer) 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) } diff --git a/infrastructure/claims/hydra_claims.go b/infrastructure/claims/hydra_claims.go index 6389253..c89b254 100644 --- a/infrastructure/claims/hydra_claims.go +++ b/infrastructure/claims/hydra_claims.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/pem" "errors" + "fmt" "oc-auth/conf" "oc-auth/infrastructure/perms_connectors" "oc-auth/infrastructure/utils" @@ -119,21 +120,23 @@ func (h HydraClaims) DecodeClaimsInToken(host string, method string, forward str Relation: "permits" + strings.ToUpper(meth.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") } // add claims to token method of HydraTokenizer -func (h HydraClaims) AddClaimsToToken(userId string, p *peer.Peer) Claims { +func (h HydraClaims) AddClaimsToToken(clientID string, userId string, p *peer.Peer) Claims { claims := Claims{} perms, err := perms_connectors.KetoConnector{}.GetPermissionByUser(userId, true) + if err != nil { return claims } claims.Session.AccessToken = make(map[string]interface{}) claims.Session.IDToken = make(map[string]interface{}) + fmt.Println("PERMS err 1", perms, err) for _, perm := range perms { key, err := h.generateKey(strings.ReplaceAll(perm.Relation, "permits", ""), perm.Subject) if err != nil { @@ -145,15 +148,15 @@ func (h HydraClaims) AddClaimsToToken(userId string, p *peer.Peer) Claims { if err != nil { 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 return claims } - -// add signature in the token MISSING diff --git a/infrastructure/infrastructure.go b/infrastructure/infrastructure.go index 1d78dcc..b08768c 100644 --- a/infrastructure/infrastructure.go +++ b/infrastructure/infrastructure.go @@ -10,8 +10,8 @@ func GetAuthConnector() auth_connectors.AuthConnector { return auth_connectors.GetAuthConnector() } -func GetPermissionConnector() perms_connectors.PermConnector { - return perms_connectors.GetPermissionConnector() +func GetPermissionConnector(client string) perms_connectors.PermConnector { + return perms_connectors.GetPermissionConnector(client) } func GetClaims() claims.ClaimService { diff --git a/infrastructure/perms_connectors/keto_connector.go b/infrastructure/perms_connectors/keto_connector.go index 1f63c1a..0302476 100644 --- a/infrastructure/perms_connectors/keto_connector.go +++ b/infrastructure/perms_connectors/keto_connector.go @@ -6,24 +6,29 @@ import ( "fmt" "oc-auth/conf" "oc-auth/infrastructure/utils" - "strings" oclib "cloud.o-forge.io/core/oc-lib" "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 { return "open-cloud" } func (k KetoConnector) scope() string { - return "oc-auth" + return "oc-auth-realm" } func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string { - n := "?namespace=" + perm.Namespace() + n := "?namespace=" + f.namespace() if perm.Object != "" { n += "&object=" + perm.Object } @@ -189,6 +194,7 @@ func (k KetoConnector) GetPermissionByRole(roleID string) ([]Permission, error) } func (k KetoConnector) GetPermissionByUser(userID string, internal bool) ([]Permission, error) { roles, err := k.get("", "member", userID) + fmt.Println("ROLES", roles, err) if err != nil { return nil, err } @@ -235,7 +241,7 @@ func (k KetoConnector) get(object string, relation string, subject string) ([]Pe return t, nil } -func (k KetoConnector) binds(subject string, relation string, object string) (string, int, error) { +func (k KetoConnector) binds(object string, relation string, subject string) (string, int, error) { _, code, err := k.createRelationShip(object, relation, subject, nil) if err != nil { return object, code, err @@ -244,6 +250,7 @@ func (k KetoConnector) binds(subject string, relation string, object string) (st } func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) { + fmt.Println("BIND ROLE", userID, roleID) return k.binds(userID, "member", roleID) } @@ -324,9 +331,6 @@ func (k KetoConnector) UnBindPermission(roleID string, permID string, relation s } func (k KetoConnector) createRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) { exist, err := k.get(object, relation, subject) - if strings.Contains(subject, "/workflow/:id") { - fmt.Println("subject", subject, relation, exist, err) - } if err == nil && len(exist) > 0 { return nil, 409, errors.New("Relation already exist") } @@ -338,7 +342,7 @@ func (k KetoConnector) createRelationShip(object string, relation string, subjec if err != nil { 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 port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorAdminPort) diff --git a/infrastructure/perms_connectors/perms_connector.go b/infrastructure/perms_connectors/perms_connector.go index b8d3ba7..9560725 100644 --- a/infrastructure/perms_connectors/perms_connector.go +++ b/infrastructure/perms_connectors/perms_connector.go @@ -1,6 +1,8 @@ package perms_connectors import ( + "oc-auth/conf" + "cloud.o-forge.io/core/oc-lib/tools" ) @@ -21,6 +23,7 @@ func (k Permission) Scope() string { type PermConnector interface { Status() tools.State + SetClient(scope string) CheckPermission(perm Permission, permDependancies *Permission, internal bool) bool BindRole(userID string, roleID string) (string, int, error) BindGroup(userID string, groupID string) (string, int, error) @@ -51,6 +54,6 @@ var c = map[string]PermConnector{ "keto": KetoConnector{}, } -func GetPermissionConnector() PermConnector { - return c["keto"] +func GetPermissionConnector(scope string) PermConnector { + return c[conf.GetConfig().PermissionConnectorHost] } diff --git a/ldap-hydra/docker-compose-2.yml b/ldap-hydra/docker-compose-2.yml new file mode 100644 index 0000000..2fe27d0 --- /dev/null +++ b/ldap-hydra/docker-compose-2.yml @@ -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 diff --git a/ldap-hydra/ldap-2.ldif b/ldap-hydra/ldap-2.ldif new file mode 100644 index 0000000..79ac6b6 --- /dev/null +++ b/ldap-hydra/ldap-2.ldif @@ -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 \ No newline at end of file diff --git a/main.go b/main.go index 1a808f1..f134847 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,12 @@ package main import ( + "context" "errors" + "fmt" "oc-auth/conf" "oc-auth/infrastructure" + auth_connectors "oc-auth/infrastructure/auth_connector" _ "oc-auth/routers" "os" "strconv" @@ -45,6 +48,7 @@ func main() { conf.GetConfig().PermissionConnectorAdminPort = o.GetIntDefault("PERMISSION_CONNECTOR_ADMIN_PORT", 4467) // config LDAP + conf.GetConfig().SourceMode = o.GetStringDefault("SOURCE_MODE", "ldap") conf.GetConfig().LDAPEndpoints = o.GetStringDefault("LDAP_ENDPOINTS", "ldap:389") conf.GetConfig().LDAPBindDN = o.GetStringDefault("LDAP_BINDDN", "cn=admin,dc=example,dc=com") conf.GetConfig().LDAPBindPW = o.GetStringDefault("LDAP_BINDPW", "password") @@ -54,6 +58,7 @@ func main() { if err != nil { panic(err) } + generateRole() discovery() beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ AllowAllOrigins: true, @@ -65,6 +70,31 @@ func main() { 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 { // TODO check if files at private & public path are set // check if files at private & public path are set @@ -75,7 +105,7 @@ func generateSelfPeer() error { return errors.New("public key path does not exist") } // 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) file := "" f, err := os.ReadFile(conf.GetConfig().PublicKeyPath) if err != nil { @@ -100,7 +130,7 @@ func generateSelfPeer() error { PublicKey: file, 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 != "" { return errors.New(data.Err) } @@ -109,7 +139,7 @@ func generateSelfPeer() error { func discovery() { api := tools.API{} - conn := infrastructure.GetPermissionConnector() + conn := infrastructure.GetPermissionConnector("") conn.CreateRole(conf.GetConfig().AdminRole) conn.BindRole(conf.GetConfig().AdminRole, "admin") diff --git a/routers/commentsRouter.go b/routers/commentsRouter.go index e18f62f..2c3767f 100644 --- a/routers/commentsRouter.go +++ b/routers/commentsRouter.go @@ -81,7 +81,7 @@ func init() { beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.ControllerComments{ - Method: "InternalAuthForward", + Method: "InternaisDraftlAuthForward", Router: `/forward`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), @@ -99,8 +99,8 @@ func init() { beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.ControllerComments{ - Method: "LoginLDAP", - Router: `/ldap/login`, + Method: "Login", + Router: `/login`, AllowHTTPMethods: []string{"post"}, MethodParams: param.Make(), Filters: nil, @@ -108,8 +108,8 @@ func init() { beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"], beego.ControllerComments{ - Method: "LogOutLDAP", - Router: `/ldap/logout`, + Method: "LogOut", + Router: `/logout`, AllowHTTPMethods: []string{"delete"}, MethodParams: param.Make(), Filters: nil, diff --git a/swagger/swagger.json b/swagger/swagger.json index 77a837c..9b17f52 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -191,7 +191,7 @@ "parameters": [ { "in": "path", - "name": "group_id", + "name": "user_id", "description": "The group_id you want to unbind", "required": true, "type": "string" @@ -233,7 +233,7 @@ } } }, - "/ldap/login": { + "/login": { "post": { "tags": [ "oc-auth/controllersOAuthController" @@ -249,6 +249,13 @@ "schema": { "$ref": "#/definitions/models.workflow" } + }, + { + "in": "query", + "name": "client_id", + "description": "the client_id you want to get", + "required": true, + "type": "string" } ], "responses": { @@ -258,7 +265,7 @@ } } }, - "/ldap/logout": { + "/logout": { "delete": { "tags": [ "oc-auth/controllersOAuthController" @@ -271,6 +278,13 @@ "name": "Authorization", "description": "auth token", "type": "string" + }, + { + "in": "query", + "name": "client_id", + "description": "the client_id you want to get", + "required": true, + "type": "string" } ], "responses": { @@ -465,6 +479,13 @@ "schema": { "$ref": "#/definitions/models.Token" } + }, + { + "in": "query", + "name": "client_id", + "description": "the client_id you want to get", + "required": true, + "type": "string" } ], "responses": { diff --git a/swagger/swagger.yml b/swagger/swagger.yml index fd70eef..bc87b31 100644 --- a/swagger/swagger.yml +++ b/swagger/swagger.yml @@ -119,7 +119,7 @@ paths: operationId: GroupController.UnBind parameters: - in: path - name: group_id + name: user_id description: The group_id you want to unbind required: true type: string @@ -175,7 +175,7 @@ paths: responses: "200": description: '{string}' - /ldap/login: + /login: post: tags: - oc-auth/controllersOAuthController @@ -190,10 +190,15 @@ paths: required: true schema: $ref: '#/definitions/models.workflow' + - in: query + name: client_id + description: the client_id you want to get + required: true + type: string responses: "200": description: '{string}' - /ldap/logout: + /logout: delete: tags: - oc-auth/controllersOAuthController @@ -206,6 +211,11 @@ paths: name: Authorization description: auth token type: string + - in: query + name: client_id + description: the client_id you want to get + required: true + type: string responses: "200": description: '{string}' @@ -350,6 +360,11 @@ paths: required: true schema: $ref: '#/definitions/models.Token' + - in: query + name: client_id + description: the client_id you want to get + required: true + type: string responses: "200": description: '{string}'