Merge branch 'feature/payment' into main

This commit is contained in:
mr 2025-02-13 10:32:46 +01:00
commit 9bb08fc961
22 changed files with 481 additions and 110 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

View File

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

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)
ldap := auth_connectors.New() if conf.GetConfig().SourceMode == "ldap" {
found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password) ldap := auth_connectors.New()
if err != nil || !found { found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password)
o.Data["json"] = err fmt.Println("found", found, "err", err)
o.Ctx.ResponseWriter.WriteHeader(401) if err != nil || !found {
o.ServeJSON() o.Data["json"] = err
return 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 &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

@ -5,7 +5,7 @@ go 1.23.0
toolchain go1.23.3 toolchain go1.23.3
require ( 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/beego/beego/v2 v2.3.1
github.com/smartystreets/goconvey v1.7.2 github.com/smartystreets/goconvey v1.7.2
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
@ -17,6 +17,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/biter777/countries v1.7.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.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/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/nats.go v1.37.0 // indirect github.com/nats-io/nats.go v1.37.0 // indirect
github.com/pkg/errors v0.9.1 // 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/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
go.uber.org/multierr v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect

10
go.sum
View File

@ -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-20250205160221-88b7cfe2fd0f h1:6V+Z81ywYoDYSVMnM4PVaJYXFgCN3xSG3ddiUPn4jL8=
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/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 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=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/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 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/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/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 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 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 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 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= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=

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

@ -30,7 +30,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
@ -92,7 +91,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], ".")
@ -101,11 +100,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) {
@ -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...) 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") resp, err := a.Caller.CallGet(a.getPath(true, false), "/clients")
if err != nil { if err != nil {
return "" return ""
@ -151,11 +150,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...)
@ -220,7 +225,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")
} }
@ -228,7 +233,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)) 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)
@ -286,7 +292,8 @@ func (a HydraConnector) getClientset() (*kubernetes.Clientset, error) {
return kubernetes.NewForConfig(config) 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, ".") access := strings.Split(token, ".")
if len(access) > 2 { if len(access) > 2 {
token = strings.Join(access[0: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" 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

@ -8,7 +8,7 @@ import (
// Tokenizer interface // Tokenizer interface
type ClaimService 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) 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"
@ -119,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, p *peer.Peer) 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 {
@ -145,15 +148,15 @@ func (h HydraClaims) AddClaimsToToken(userId string, p *peer.Peer) Claims {
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.IDToken["username"] = userId
claims.Session.IDToken["peer_id"] = p.UUID claims.Session.IDToken["peer_id"] = p.UUID
// we should get group from user // we should get group from user
groups, err := perms_connectors.KetoConnector{}.GetGroupByUser(userId) groups, err := perms_connectors.KetoConnector{}.GetGroupByUser(userId)
if err != nil { if err != nil {
return claims return claims
} }
claims.Session.IDToken["client_id"] = clientID
claims.Session.IDToken["groups"] = groups claims.Session.IDToken["groups"] = groups
claims.Session.IDToken["signature"] = sign claims.Session.IDToken["signature"] = sign
return claims return claims
} }
// add signature in the token MISSING

View File

@ -10,8 +10,8 @@ func GetAuthConnector() auth_connectors.AuthConnector {
return auth_connectors.GetAuthConnector() return auth_connectors.GetAuthConnector()
} }
func GetPermissionConnector() 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

@ -6,24 +6,29 @@ import (
"fmt" "fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure/utils" "oc-auth/infrastructure/utils"
"strings"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
) )
type KetoConnector struct{} type KetoConnector struct {
Client string
}
func (k KetoConnector) SetClient(client string) {
k.Client = client
}
func (k KetoConnector) namespace() string { func (k KetoConnector) namespace() string {
return "open-cloud" return "open-cloud"
} }
func (k KetoConnector) scope() string { func (k KetoConnector) scope() string {
return "oc-auth" 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
} }
@ -189,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
} }
@ -235,7 +241,7 @@ func (k KetoConnector) get(object string, relation string, subject string) ([]Pe
return t, nil 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) _, code, err := k.createRelationShip(object, relation, subject, nil)
if err != nil { if err != nil {
return object, code, err 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) { func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) {
fmt.Println("BIND ROLE", userID, roleID)
return k.binds(userID, "member", roleID) return k.binds(userID, "member", roleID)
} }
@ -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) { func (k KetoConnector) createRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) {
exist, err := k.get(object, relation, subject) exist, err := k.get(object, relation, subject)
if strings.Contains(subject, "/workflow/:id") {
fmt.Println("subject", subject, relation, exist, err)
}
if err == nil && len(exist) > 0 { if err == nil && len(exist) > 0 {
return nil, 409, errors.New("Relation already exist") return nil, 409, errors.New("Relation already exist")
} }
@ -338,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

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

36
main.go
View File

@ -1,9 +1,12 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure" "oc-auth/infrastructure"
auth_connectors "oc-auth/infrastructure/auth_connector"
_ "oc-auth/routers" _ "oc-auth/routers"
"os" "os"
"strconv" "strconv"
@ -45,6 +48,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")
@ -54,6 +58,7 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
generateRole()
discovery() discovery()
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowAllOrigins: true, AllowAllOrigins: true,
@ -65,6 +70,31 @@ func main() {
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
@ -75,7 +105,7 @@ func generateSelfPeer() error {
return errors.New("public key path does not exist") return errors.New("public key path does not exist")
} }
// check if peer already exists // check if peer already exists
p := oclib.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 := "" file := ""
f, err := os.ReadFile(conf.GetConfig().PublicKeyPath) f, err := os.ReadFile(conf.GetConfig().PublicKeyPath)
if err != nil { if err != nil {
@ -100,7 +130,7 @@ func generateSelfPeer() error {
PublicKey: file, 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)
} }
@ -109,7 +139,7 @@ func generateSelfPeer() error {
func discovery() { func discovery() {
api := tools.API{} api := tools.API{}
conn := infrastructure.GetPermissionConnector() conn := infrastructure.GetPermissionConnector("")
conn.CreateRole(conf.GetConfig().AdminRole) conn.CreateRole(conf.GetConfig().AdminRole)
conn.BindRole(conf.GetConfig().AdminRole, "admin") conn.BindRole(conf.GetConfig().AdminRole, "admin")

View File

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

@ -191,7 +191,7 @@
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "group_id", "name": "user_id",
"description": "The group_id you want to unbind", "description": "The group_id you want to unbind",
"required": true, "required": true,
"type": "string" "type": "string"
@ -233,7 +233,7 @@
} }
} }
}, },
"/ldap/login": { "/login": {
"post": { "post": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@ -249,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": {
@ -258,7 +265,7 @@
} }
} }
}, },
"/ldap/logout": { "/logout": {
"delete": { "delete": {
"tags": [ "tags": [
"oc-auth/controllersOAuthController" "oc-auth/controllersOAuthController"
@ -271,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": {
@ -465,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": {

View File

@ -119,7 +119,7 @@ paths:
operationId: GroupController.UnBind operationId: GroupController.UnBind
parameters: parameters:
- in: path - in: path
name: group_id name: user_id
description: The group_id you want to unbind description: The group_id you want to unbind
required: true required: true
type: string type: string
@ -175,7 +175,7 @@ paths:
responses: responses:
"200": "200":
description: '{string}' description: '{string}'
/ldap/login: /login:
post: post:
tags: tags:
- oc-auth/controllersOAuthController - oc-auth/controllersOAuthController
@ -190,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
@ -206,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}'
@ -350,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}'