21 Commits

Author SHA1 Message Date
mr
0989aeb979 neo oc-lib 2025-02-06 08:56:30 +01:00
mr
8f4e33ab80 neo oc lib 2025-02-05 08:43:17 +01:00
mr
b84c2ef353 workin oc-auth 2025-01-17 17:24:08 +01:00
mr
fd65220b91 add groups in claims 2024-11-27 12:36:37 +01:00
mr
1722980514 simplify code 2024-11-27 11:54:25 +01:00
mr
01daaae766 Add Group To Keto 2024-11-27 11:12:46 +01:00
mr
be071ec328 oclib + perms naming 2024-11-21 11:07:19 +01:00
mr
9a86604564 test 2024-11-18 15:16:58 +01:00
mr
cc91341547 public key bug 2024-11-18 14:58:26 +01:00
mr
2a8349b0c7 new peer url 2024-11-15 09:40:24 +01:00
mr
f4154136e1 oclib 2024-11-08 14:00:34 +01:00
mr
c73bd264cb oclib 2024-11-07 13:43:01 +01:00
mr
d229d92b3b after test 2024-11-05 10:11:39 +01:00
mr
8b8e5d92d7 auth 2024-11-04 14:30:20 +01:00
mr
828122a5a9 auth 2024-11-04 14:24:52 +01:00
mr
605327e5c7 debugging claims 2024-11-04 09:43:35 +01:00
mr
02767d87fa casual debug for claims 2024-10-30 17:05:12 +01:00
mr
2ca16c07b3 INTERNAL ASK RULES 2024-10-30 16:39:52 +01:00
mr
d33d2eb343 OC-AUTH with admin persona 2024-10-30 16:18:21 +01:00
mr
d87883b57f BAHAMAS 2024-10-30 12:38:25 +01:00
mr
7198c40d30 Oc Auth x Hydra x LDAP : draft of claims enrich for traefik + draft of forwarding 2024-10-28 14:58:11 +01:00
43 changed files with 5262 additions and 648 deletions

View File

@@ -1,5 +1,8 @@
FROM golang:alpine as builder FROM golang:alpine as builder
ARG HOSTNAME=http://localhost
ARG NAME=local
WORKDIR /app WORKDIR /app
COPY . . COPY . .

BIN
__debug_bin142225022 Executable file

Binary file not shown.

View File

@@ -3,10 +3,27 @@ package conf
import "sync" import "sync"
type Config struct { type Config struct {
NatsUrl string SourceMode string
NatsLogin string AdminRole string
NatsPassword string PublicKeyPath string
OidcUrl string PrivateKeyPath string
LDAPEndpoints string
LDAPBindDN string
LDAPBindPW string
LDAPBaseDN string
LDAPRoleBaseDN string
ClientSecret string
Auth string
AuthConnectorHost string
AuthConnectorPort int
AuthConnectorAdminPort int
PermissionConnectorHost string
PermissionConnectorPort int
PermissionConnectorAdminPort int
} }
var instance *Config var instance *Config

View File

@@ -1,84 +0,0 @@
package controllers
import (
"fmt"
"net/http"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about auth
type AuthController struct {
beego.Controller
}
// @Title Get
// @Description find auth by authid
// @Param authId path string true "the authid you want to get"
// @Success 200 {auth} models.auth
// @Failure 403 :authId is empty
// @router /discover/:url [get]
func (o *AuthController) GetConfig() {
url := o.Ctx.Input.Param(":url")
response, err := http.Get(url + "/.well-known/openid-configuration")
if err != nil {
fmt.Println(err)
}
fmt.Println(url)
// read response body
data := make([]byte, 1024)
_, err = response.Body.Read(data)
if err != nil {
fmt.Println(err)
}
o.Data["json"] = data
o.ServeJSON()
}
// @Title Create
// @Description create auths
// @Param body body []models.auth true "The auth content"
// @Success 200 {string} models.auth.Id
// @Failure 403 body is empty
// @router / [post]
func (o *AuthController) Post() {
// store and return Id or post with UUID
o.Data["json"] = map[string]string{"Id": "?"}
o.ServeJSON()
}
// @Title Get
// @Description find auth by authid
// @Param authId path string true "the authid you want to get"
// @Success 200 {auth} models.auth
// @Failure 403 :authId is empty
// @router /:authId [get]
func (o *AuthController) Get() {
authId := o.Ctx.Input.Param(":authId")
fmt.Println(authId)
o.ServeJSON()
}
// @Title Find
// @Description find auths with query
// @Param query path string true "the keywords you need"
// @Success 200 {auths} []models.auth
// @Failure 403
// @router /find/:query [get]
func (o *AuthController) Find() {
query := o.Ctx.Input.Param(":query")
fmt.Println(query)
o.ServeJSON()
}
// @Title Delete
// @Description delete the auth
// @Param authId path string true "The authId you want to delete"
// @Success 200 {string} delete success!
// @Failure 403 authId is empty
// @router /:authId [delete]
func (o *AuthController) Delete() {
authId := o.Ctx.Input.Param(":authId")
fmt.Println(authId)
o.ServeJSON()
}

221
controllers/group.go Normal file
View File

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

233
controllers/oauth2.go Normal file
View File

@@ -0,0 +1,233 @@
package controllers
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"oc-auth/conf"
"oc-auth/infrastructure"
auth_connectors "oc-auth/infrastructure/auth_connector"
"regexp"
"strings"
oclib "cloud.o-forge.io/core/oc-lib"
model "cloud.o-forge.io/core/oc-lib/models/peer"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about auth
type OAuthController struct {
beego.Controller
}
// @Title Logout
// @Description unauthenticate user
// @Param Authorization header string false "auth token"
// @Param client_id query string true "the client_id you want to get"
// @Success 200 {string}
// @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 {
reqToken = ""
} else {
reqToken = splitToken[1]
}
var res auth_connectors.Token
json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res)
token, err := infrastructure.GetAuthConnector().Logout(clientID, reqToken)
if err != nil || token == nil {
o.Data["json"] = err
} else {
o.Data["json"] = token
}
o.ServeJSON()
}
// @Title Login
// @Description authenticate user
// @Param body body models.workflow true "The workflow content"
// @Param client_id query string true "the client_id you want to get"
// @Success 200 {string}
// @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)
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(
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)
} else {
o.Data["json"] = token
}
o.ServeJSON()
}
// @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(clientID, &token)
if err != nil || newToken == nil {
o.Data["json"] = err
o.Ctx.ResponseWriter.WriteHeader(401)
} else {
o.Data["json"] = newToken
}
o.ServeJSON()
}
// @Title Introspection
// @Description introspect token
// @Param Authorization header string false "auth token"
// @Success 200 {string}
// @router /introspect [get]
func (o *OAuthController) Introspect() {
reqToken := o.Ctx.Request.Header.Get("Authorization")
splitToken := strings.Split(reqToken, "Bearer ")
if len(splitToken) < 2 {
reqToken = ""
} else {
reqToken = splitToken[1]
}
token, err := infrastructure.GetAuthConnector().Introspect(reqToken)
if err != nil || !token {
o.Data["json"] = err
o.Ctx.ResponseWriter.WriteHeader(401)
}
o.ServeJSON()
}
var whitelist = []string{
"/login",
"/refresh",
"/introspect",
}
// @Title AuthForward
// @Description auth forward
// @Param Authorization header string false "auth token"
// @Success 200 {string}
// @router /forward [get]
func (o *OAuthController) InternaisDraftlAuthForward() {
fmt.Println("InternalAuthForward")
reqToken := o.Ctx.Request.Header.Get("Authorization")
if reqToken == "" {
for _, w := range whitelist {
if strings.Contains(o.Ctx.Request.Header.Get("X-Forwarded-Uri"), w) {
o.Ctx.ResponseWriter.WriteHeader(200)
o.ServeJSON()
return
}
}
o.Ctx.ResponseWriter.WriteHeader(401)
o.ServeJSON()
return
}
splitToken := strings.Split(reqToken, "Bearer ")
if len(splitToken) < 2 {
reqToken = ""
} else {
reqToken = splitToken[1]
}
origin, publicKey, external := o.extractOrigin(o.Ctx.Request)
if !infrastructure.GetAuthConnector().CheckAuthForward( //reqToken != "" &&
reqToken, publicKey, origin,
o.Ctx.Request.Header.Get("X-Forwarded-Method"),
o.Ctx.Request.Header.Get("X-Forwarded-Uri"), external) && origin != "" && publicKey != "" {
o.Ctx.ResponseWriter.WriteHeader(401)
o.ServeJSON()
return
}
o.ServeJSON()
}
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")
if origin == "" {
origin = o.Ctx.Request.Header.Get("Origin")
}
searchStr := origin
r := regexp.MustCompile("(:[0-9]+)")
t := r.FindString(searchStr)
if t != "" {
searchStr = strings.Replace(searchStr, t, "", -1)
}
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
}
p := peer.Data[0].(*model.Peer)
publicKey = p.PublicKey
origin = p.Url
if origin != "" { // is external
if strings.Contains(origin, "localhost") || strings.Contains(origin, "127.0.0.1") || p.State == model.SELF {
external = false
}
} else {
external = false
}
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 ""
}

199
controllers/permission.go Normal file
View File

@@ -0,0 +1,199 @@
package controllers
import (
"oc-auth/infrastructure"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about auth
type PermissionController struct {
beego.Controller
}
// @Title GetAll
// @Description find permissions
// @Success 200 {permission} string
// @router / [get]
func (o *PermissionController) GetAll() {
clientID := ExtractClient(*o.Ctx.Request)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermission("", "")
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title GetByRole
// @Description find permission by role id
// @Param id path string true "the id you want to get"
// @Success 200 {auth} string
// @router /role/:id [get]
func (o *PermissionController) GetByRole() {
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermissionByRole(id)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title GetByUser
// @Description find permission by user id
// @Param id path string true "the id you want to get"
// @Success 200 {auth} string
// @router /user/:id [get]
func (o *PermissionController) GetByUser() {
id := o.Ctx.Input.Param(":id")
clientID := ExtractClient(*o.Ctx.Request)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermissionByUser(id, true)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Get
// @Description find auth by permission
// @Param id path string true "the permission you want to get"
// @Success 200 {auth} models.auth
// @router /:id/:relation [get]
func (o *PermissionController) Get() {
id := o.Ctx.Input.Param(":id")
rel := o.Ctx.Input.Param(":relation")
clientID := ExtractClient(*o.Ctx.Request)
role, err := infrastructure.GetPermissionConnector(clientID).GetPermission(id, rel)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": 200,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Clear
// @Description clear the permission
// @Success 200 {string} delete success!
// @router /clear [delete]
func (o *PermissionController) Clear() {
clientID := ExtractClient(*o.Ctx.Request)
role, code, err := infrastructure.GetPermissionConnector(clientID).DeletePermission("", "", true)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title Bind
// @Description bind the permission to role
// @Param role_id path string true "The role_id you want to bind"
// @Param method path string true "The method you want to relate role & permission"
// @Param permission_id path string true "The permission_id you want to bind"
// @Success 200 {string} bind success!
// @router /:permission_id/:role_id/:relation [post]
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")
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,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}
// @Title UnBind
// @Description unbind the permission to role
// @Param role_id path string true "The role_id you want to unbind"
// @Param relation path string true "The method you want to unrelate role & permission"
// @Param permission_id path string true "The permission_id you want to unbind"
// @Success 200 {string} bind success!
// @router /:permission_id/:role_id/:relation [delete]
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")
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,
"error": err.Error(),
"code": code,
}
} else {
o.Data["json"] = map[string]interface{}{
"data": role,
"error": nil,
"code": 200,
}
}
o.ServeJSON()
}

View File

@@ -1,52 +0,0 @@
package controllers
import (
"encoding/json"
"log"
"oc-auth/models"
"strings"
beego "github.com/beego/beego/v2/server/web"
"github.com/nats-io/nats.go"
)
// Operations about auth
type RegistrationController struct {
beego.Controller
}
// @Title Create
// @Description create auths
// @Param body body models.Application true "The app info"
// @Success 200 {string} models.auth.Id
// @Failure 403 body is empty
// @router / [post]
func (o *RegistrationController) Post() {
var app models.Application
// Store the app info in the nats server
err := json.Unmarshal(o.Ctx.Input.RequestBody, &app)
if err != nil {
log.Fatal(err)
}
servers := []string{"nats://127.0.0.1:1222", "nats://127.0.0.1:1223", "nats://127.0.0.1:1224"}
nc, err := nats.Connect(strings.Join(servers, ","))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
js, err := nc.JetStream(nats.PublishAsyncMaxPending(256))
if err != nil {
log.Fatal(err)
}
kv, err := js.KeyValue("auth")
if err != nil {
log.Fatal(err)
}
kv.Put(app.ClientId, o.Ctx.Input.RequestBody)
// register to OIDC server
// store and return Id or post with UUID
o.Data["json"] = map[string]string{"Id": "?"}
o.ServeJSON()
}

221
controllers/role.go Normal file
View File

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

View File

@@ -17,3 +17,12 @@ func (c *VersionController) GetAll() {
c.Data["json"] = map[string]string{"version": "1"} c.Data["json"] = map[string]string{"version": "1"}
c.ServeJSON() c.ServeJSON()
} }
// @Title Get
// @Description get version
// @Success 200
// @router /discovery [get]
func (c *VersionController) Get() {
c.Data["json"] = map[string]string{"version": "1"}
c.ServeJSON()
}

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

View File

@@ -1,10 +1,41 @@
version: '3.4' version: '3.4'
services: services:
ocauth: traefik:
image: 'ocauth:latest' image: traefik:v2.10.4
container_name: traefik
networks:
- catalog
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--log.level=DEBUG"
ports: ports:
- 8088:8080 - "8080:80"
container_name: ocauth - "8082:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
oc-auth:
image: 'oc-auth:latest'
ports:
- 8094:8080
container_name: oc-auth
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.auth.forwardauth.address=http://oc-auth:8080/oc/forward"
- "traefik.http.routers.workflow.rule=PathPrefix(/auth)"
environment:
LDAP_ENDPOINTS: ldap: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

View File

@@ -1,6 +1,10 @@
{ {
"natsurl":"http://localhost:4080", "MONGO_URL":"mongodb://mongo:27017/",
"login":"admin", "MONGO_DATABASE":"DC_myDC",
"password":"admin", "NATS_URL": "nats://nats:4222",
"oidcserver":"http://localhost:8080" "PORT" : 8080,
"AUTH_CONNECTOR_HOST": "hydra",
"PRIVATE_KEY_PATH": "/etc/oc/pem/private.pem",
"PUBLIC_KEY_PATH": "/etc/oc/pem/public.pem",
"LDAP_ENDPOINTS": "ldap:389"
} }

130
go.mod
View File

@@ -3,59 +3,135 @@ module oc-auth
go 1.22.0 go 1.22.0
require ( require (
cloud.o-forge.io/core/oc-lib v0.0.0-20240927065314-0ac55a0ec151 cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f
github.com/beego/beego/v2 v2.0.7 github.com/beego/beego/v2 v2.3.1
github.com/nats-io/nats.go v1.37.0 github.com/nats-io/nats.go v1.37.0
github.com/ory/hydra-client-go v1.11.8 github.com/ory/hydra-client-go v1.11.8
github.com/smartystreets/goconvey v1.7.2 github.com/smartystreets/goconvey v1.7.2
golang.org/x/oauth2 v0.5.0 go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.23.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/biter777/countries v1.7.5 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gobuffalo/pop/v6 v6.0.8 // indirect
github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/marcinwyszynski/geopoint v0.0.0-20140302213024-cf2a6f750c5b // indirect
github.com/mattn/goveralls v0.0.12 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe // indirect
github.com/ory/go-convenience v0.1.0 // indirect
github.com/ory/x v0.0.575 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.42.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.11.0 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/coocood/freecache v1.2.4
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/goraz/onion v0.1.3 // indirect github.com/goraz/onion v0.1.3 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/i-core/rlog v1.0.0
github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/klauspost/compress v1.17.9 // indirect github.com/justinas/nosurf v1.1.1
github.com/kr/pretty v0.3.1 // indirect github.com/kelseyhightower/envconfig v1.4.0
github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect github.com/nats-io/nuid v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/ory/fosite v0.47.0
github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.41.0 // indirect github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.9.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/purnaresa/bulwark v0.0.0-20201001150757-1cec324746b2
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/zerolog v1.33.0 // indirect github.com/rs/zerolog v1.33.0 // indirect
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/assertions v1.2.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver v1.16.0 // indirect go.mongodb.org/mongo-driver v1.17.1 // indirect
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.27.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.19.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

799
go.sum

File diff suppressed because it is too large Load Diff

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
Hostname: a3ff4096def1
IP: 127.0.0.1
IP: 172.18.0.16
RemoteAddr: 172.18.0.1:46314
GET / HTTP/1.1
Host: localhost:5000
User-Agent: Wget/1.20.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Connection: Keep-Alive
X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb

View File

@@ -0,0 +1,41 @@
package auth_connectors
import (
"net/http"
"oc-auth/conf"
"cloud.o-forge.io/core/oc-lib/tools"
)
type AuthConnector interface {
Status() tools.State
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(client_id string, token *Token) (*Token, error)
CheckAuthForward(reqToken string, publicKey string, host string, method string, forward string, external bool) bool
}
type Token struct {
Active bool `json:"active"`
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
TokenType string `json:"token_type"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
type Redirect struct {
RedirectTo string `json:"redirect_to"`
}
var a = map[string]AuthConnector{
"hydra": HydraConnector{
Caller: tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{}),
State: "12345678", ResponseType: "token", Scopes: "openid profile email roles"}, // base url
}
func GetAuthConnector() AuthConnector {
return a[conf.GetConfig().Auth]
}

View File

@@ -0,0 +1,287 @@
package auth_connectors
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"oc-auth/conf"
"oc-auth/infrastructure/claims"
"regexp"
"strconv"
"strings"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/tools"
)
type HydraConnector struct {
State string `json:"state"`
Scopes string `json:"scope"`
ResponseType string `json:"response_type"`
Caller *tools.HTTPCaller
}
func (a HydraConnector) Status() tools.State {
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{})
var responseBody map[string]interface{}
host := conf.GetConfig().AuthConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().AuthConnectorPort)
resp, err := caller.CallGet("http://"+host+":"+port, "/health/ready")
if err != nil {
return tools.DEAD
}
err = json.Unmarshal(resp, &responseBody)
if err != nil || responseBody["status"] != "ok" {
return tools.DEAD
}
return tools.ALIVE
}
// urlFormat formats the URL of the peer with the data type API function
func (a *HydraConnector) urlFormat(url string, replaceWith string) string {
// localhost is replaced by the local peer URL
// because localhost must collide on a web request security protocol
r := regexp.MustCompile("(http://[a-z]+:[0-9]+)/oauth2")
t := r.FindString(url)
if t != "" {
url = strings.Replace(url, t, replaceWith, -1)
}
return url
}
func (a HydraConnector) challenge(username string, url string, challenge string, cookies ...*http.Cookie) (*Redirect, string, []*http.Cookie, error) {
body := map[string]interface{}{
"remember_for": 0,
"remember": true,
}
if challenge != "consent" {
body["subject"] = username
}
s := strings.Split(url, challenge+"_challenge=")
resp, err := a.Caller.CallRaw(http.MethodPut,
a.getPath(true, true), "/auth/requests/"+challenge+"/accept?"+challenge+"_challenge="+s[1],
body, "application/json", true, cookies...) // "remember": true, "subject": username
if err != nil {
return nil, s[1], cookies, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, s[1], cookies, err
}
var token Redirect
err = json.Unmarshal(b, &token)
if err != nil {
return nil, s[1], cookies, err
}
return &token, s[1], cookies, nil
}
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], ".")
}
isValid, err := a.Introspect(token.AccessToken)
if err != nil || !isValid {
return nil, err
}
_, err = a.Logout(client_id, token.AccessToken)
if err != nil {
return nil, err
}
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) {
resp, err := a.Caller.CallRaw(http.MethodGet, url, subpath,
map[string]interface{}{}, "application/json", true, cookies...)
if err != nil || resp.Request.Response == nil || resp.Request.Response.Header["Set-Cookie"] == nil {
return nil, "", cookies, err
}
cc := resp.Request.Response.Header["Set-Cookie"] // retrieve oauth2 csrf token cookie
if len(cc) > 0 {
for _, c := range cc {
first := strings.Split(c, ";")
cookies = append(cookies, &http.Cookie{
Name: strings.Split(first[0], "=")[0],
Value: strings.ReplaceAll(first[0], strings.Split(first[0], "=")[0]+"=", ""),
})
}
}
return a.challenge(username, resp.Request.URL.String(), challenge, cookies...)
}
func (a HydraConnector) getClient(clientID string) string {
resp, err := a.Caller.CallGet(a.getPath(true, false), "/clients")
if err != nil {
return ""
}
var clients []interface{}
err = json.Unmarshal(resp, &clients)
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(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...)
if err != nil || redirect == nil {
return nil, err
}
redirect, _, cookies, err = a.tryLog(username, a.urlFormat(redirect.RedirectTo, a.getPath(false, true)), "", "consent", cookies...)
if err != nil || redirect == nil {
return nil, err
}
// problem with consent THERE we need to accept the consent challenge && get the token
_, err = a.Caller.CallRaw(http.MethodGet, a.urlFormat(redirect.RedirectTo, a.getPath(false, true)), "", map[string]interface{}{},
"application/json", true, cookies...)
if err != nil {
s := strings.Split(err.Error(), "\"")
if len(s) > 1 && strings.Contains(s[1], "access_token") {
err = nil
} else {
return nil, err
}
}
token := &Token{
Username: username,
}
urls := url.Values{}
urls.Add("client_id", clientID)
urls.Add("client_secret", conf.GetConfig().ClientSecret)
urls.Add("grant_type", "client_credentials")
resp, err := a.Caller.CallForm(http.MethodPost, a.getPath(false, true), "/token", urls,
"application/x-www-form-urlencoded", true, cookies...)
var m map[string]interface{}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(b, &token)
if err != nil {
return nil, err
}
json.Unmarshal(b, &m)
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")
}
now := time.Now().UTC()
now = now.Add(time.Duration(token.ExpiresIn) * time.Second)
unix := now.Unix()
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)
token.AccessToken = strings.ReplaceAll(token.AccessToken, "ory_at_", "") + "." + base64.StdEncoding.EncodeToString(b)
token.Active = true
return token, nil
}
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], ".")
}
p := a.getPath(false, true) + "/revoke"
urls := url.Values{}
urls.Add("token", token)
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 {
return nil, err
}
return &Token{
AccessToken: token,
Active: false,
}, nil
}
func (a HydraConnector) Introspect(token string, cookie ...*http.Cookie) (bool, error) {
// check validity of the token by calling introspect endpoint
// if token is not active, we need to re-authenticate by sending the user to the login page
access := strings.Split(token, ".")
if len(access) > 2 {
token = strings.Join(access[0:2], ".")
}
urls := url.Values{}
urls.Add("token", token)
resp, err := a.Caller.CallForm(http.MethodPost, a.getPath(true, true), "/introspect", urls,
"application/x-www-form-urlencoded", true, cookie...)
if err != nil || resp.StatusCode >= 300 {
return false, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
var introspect Token
err = json.Unmarshal(b, &introspect)
if err != nil {
return false, err
}
introspect.AccessToken = token
return introspect.Active, nil
}
func (a HydraConnector) getPath(isAdmin bool, isOauth bool) string {
host := conf.GetConfig().AuthConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().AuthConnectorPort)
if isAdmin {
port = fmt.Sprintf("%v", conf.GetConfig().AuthConnectorAdminPort) + "/admin"
}
oauth := ""
if isOauth {
oauth = "/oauth2"
}
return "http://" + host + ":" + port + oauth
}
func (a HydraConnector) CheckAuthForward(reqToken string, publicKey string, host string, method string, forward string, external bool) bool {
if forward == "" || method == "" {
return false
}
var c claims.Claims
token := strings.Split(reqToken, ".")
if len(token) > 2 {
bytes, err := base64.StdEncoding.DecodeString(token[2])
if err != nil {
return false
}
err = json.Unmarshal(bytes, &c)
if err != nil {
return false
}
}
// ask keto for permission is in claims
ok, err := claims.GetClaims().DecodeClaimsInToken(host, method, forward, c, publicKey, external)
if err != nil {
fmt.Println("Failed to decode claims", err)
}
return ok
}

View File

@@ -0,0 +1,482 @@
package auth_connectors
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net"
"oc-auth/conf"
"strings"
"sync"
"time"
"github.com/coocood/freecache"
"github.com/go-ldap/ldap/v3"
"github.com/i-core/rlog"
"go.uber.org/zap"
)
var (
// errInvalidCredentials is an error that happens when a user's password is invalid.
errInvalidCredentials = fmt.Errorf("invalid credentials")
// errConnectionTimeout is an error that happens when no one LDAP endpoint responds.
errConnectionTimeout = fmt.Errorf("connection timeout")
// errMissedUsername is an error that happens
errMissedUsername = errors.New("username is missed")
// errUnknownUsername is an error that happens
errUnknownUsername = errors.New("unknown username")
)
type conn interface {
Bind(bindDN, password string) 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
}
type connector interface {
Connect(ctx context.Context, addr string) (conn, error)
}
// Config is a LDAP configuration.
type Config struct {
Endpoints []string `envconfig:"endpoints" required:"true" desc:"a LDAP's server URLs as \"<address>:<port>\""`
BindDN string `envconfig:"binddn" desc:"a LDAP bind DN"`
BindPass string `envconfig:"bindpw" json:"-" desc:"a LDAP bind password"`
BaseDN string `envconfig:"basedn" required:"true" desc:"a LDAP base DN for searching users"`
AttrClaims map[string]string `envconfig:"attr_claims" default:"name:name,sn:family_name,givenName:given_name,mail:email" desc:"a mapping of LDAP attributes to OpenID connect claims"`
RoleBaseDN string `envconfig:"role_basedn" required:"true" desc:"a LDAP base DN for searching roles"`
RoleAttr string `envconfig:"role_attr" default:"description" desc:"a LDAP group's attribute that contains a role's name"`
RoleClaim string `envconfig:"role_claim" default:"https://github.com/i-core/werther/claims/roles" desc:"a name of an OpenID Connect claim that contains user roles"`
CacheSize int `envconfig:"cache_size" default:"512" desc:"a user info cache's size in KiB"`
CacheTTL time.Duration `envconfig:"cache_ttl" default:"30m" desc:"a user info cache TTL"`
IsTLS bool `envconfig:"is_tls" default:"false" desc:"should LDAP connection be established via TLS"`
FlatRoleClaims bool `envconfig:"flat_role_claims" desc:"add roles claim as single list"`
}
// New creates a new LDAP client.
func New() *Client {
cnf := Config{
Endpoints: strings.Split(conf.GetConfig().LDAPEndpoints, ","),
BindDN: conf.GetConfig().LDAPBindDN,
BindPass: conf.GetConfig().LDAPBindPW,
BaseDN: conf.GetConfig().LDAPBaseDN,
RoleBaseDN: conf.GetConfig().LDAPRoleBaseDN,
}
return &Client{
Config: cnf,
connector: &ldapConnector{BaseDN: cnf.BaseDN, RoleBaseDN: cnf.RoleBaseDN, IsTLS: cnf.IsTLS},
cache: freecache.NewCache(cnf.CacheSize * 1024),
}
}
type Client struct {
Config
connector connector
cache *freecache.Cache
}
func (cli *Client) Authenticate(ctx context.Context, username string, password string) (bool, error) {
if username == "" || password == "" {
return false, nil
}
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
cn, ok := <-cli.connect(ctx)
cancel()
if !ok {
return false, errConnectionTimeout
}
defer cn.Close()
// Find a user DN by his or her username.
details, err := cli.findBasicUserDetails(cn, username, []string{"dn"})
if err != nil {
return false, err
}
if details == nil {
return false, nil
}
a := details["dn"]
if err := cn.Bind(a[0], password); err != nil {
if err == errInvalidCredentials {
return false, nil
}
return false, err
}
// Clear the claims' cache because of possible re-authentication. We don't want stale claims after re-login.
if ok := cli.cache.Del([]byte(username)); ok {
log := rlog.FromContext(ctx)
log.Debug("Cleared user's OIDC claims in the cache")
}
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
Name string // the claim name
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 == "" {
return nil, errMissedUsername
}
log := rlog.FromContext(ctx).Sugar()
// Retrieving from LDAP is slow. So, we try to get claims for the given username from the cache.
switch cdata, err := cli.cache.Get([]byte(username)); err {
case nil:
var claims []LDAPClaim
if err = json.Unmarshal(cdata, &claims); err != nil {
log.Info("Failed to unmarshal user's OIDC claims", zap.Error(err), "data", cdata)
return nil, err
}
log.Debugw("Retrieved user's OIDC claims from the cache", "claims", claims)
return claims, nil
case freecache.ErrNotFound:
log.Debug("User's OIDC claims is not found in the cache")
default:
log.Infow("Failed to retrieve user's OIDC claims from the cache", zap.Error(err))
}
// Try to make multiple TCP connections to the LDAP server for getting claims.
// Accept the first one, and cancel others.
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
cn, ok := <-cli.connect(ctx)
cancel()
if !ok {
return nil, errConnectionTimeout
}
defer cn.Close()
// We need to find LDAP attribute's names for all required claims.
attrs := []string{"dn"}
for k := range cli.AttrClaims {
attrs = append(attrs, k)
}
// Find the attributes in the LDAP server.
details, err := cli.findBasicUserDetails(cn, username, attrs)
if err != nil {
return nil, err
}
if details == nil {
return nil, errUnknownUsername
}
log.Infow("Retrieved user's info from LDAP", "details", details)
// Transform the retrieved attributes to corresponding claims.
claims := make([]LDAPClaim, 0, len(details))
for attr, v := range details {
if claim, ok := cli.AttrClaims[attr]; ok {
claims = append(claims, LDAPClaim{claim, claim, v})
}
}
// User's roles is stored in LDAP as groups. We find all groups in a role's DN
// that include the user as a member.
entries, err := cn.SearchUserRoles(fmt.Sprintf("%s", details["dn"]), "dn", cli.RoleAttr)
if err != nil {
return nil, err
}
roles := make(map[string]interface{})
for _, entry := range entries {
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
}
// 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 {
log.Infow("A role's DN without the role's base DN must contain two nodes only",
"roleBaseDN", cli.RoleBaseDN, "roleDN", roleDN)
continue
}
appID := path[1][len("OU="):]
var appRoles []interface{}
if v := roles[appID]; v != nil {
appRoles = v.([]interface{})
}
appRoles = append(appRoles, entry[cli.RoleAttr])
roles[appID] = appRoles
}
claims = append(claims, LDAPClaim{cli.RoleClaim, cli.RoleClaim, roles})
if cli.FlatRoleClaims {
for appID, appRoles := range roles {
claims = append(claims, LDAPClaim{cli.RoleClaim, cli.RoleClaim + "/" + appID, appRoles})
}
}
// Save the claims in the cache for future queries.
cdata, err := json.Marshal(claims)
if err != nil {
log.Infow("Failed to marshal user's OIDC claims for caching", zap.Error(err), "claims", claims)
}
if err = cli.cache.Set([]byte(username), cdata, int(cli.CacheTTL.Seconds())); err != nil {
log.Infow("Failed to store user's OIDC claims into the cache", zap.Error(err), "claims", claims)
}
return claims, nil
}
func (cli *Client) connect(ctx context.Context) <-chan conn {
var (
wg sync.WaitGroup
ch = make(chan conn)
)
wg.Add(len(cli.Endpoints))
for _, addr := range cli.Endpoints {
go func(addr string) {
defer wg.Done()
cn, err := cli.connector.Connect(ctx, addr)
if err != nil {
fmt.Println("Failed to create a LDAP connection", "address", addr)
return
}
select {
case <-ctx.Done():
cn.Close()
fmt.Println("a LDAP connection is cancelled", "address", addr)
return
case ch <- cn:
}
}(addr)
}
go func() {
wg.Wait()
close(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.
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 {
return nil, errors.New(err.Error() + " : failed to login to a LDAP woth a service account")
}
}
entries, err := cn.SearchUser(username, attrs...)
if err != nil {
return nil, err
}
if len(entries) != 1 {
// We didn't find the user.
return nil, nil
}
var (
entry = entries[0]
details = make(map[string][]string)
)
for _, attr := range attrs {
if v, ok := entry[attr]; ok {
details[attr] = v
}
}
return details, nil
}
type ldapConnector struct {
BaseDN string
RoleBaseDN string
IsTLS bool
}
func (c *ldapConnector) Connect(ctx context.Context, addr string) (conn, error) {
d := net.Dialer{Timeout: ldap.DefaultTimeout}
tcpcn, err := d.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
}
if c.IsTLS {
tlscn, err := tls.DialWithDialer(&d, "tcp", addr, nil)
if err != nil {
return nil, err
}
tcpcn = tlscn
}
ldapcn := ldap.NewConn(tcpcn, c.IsTLS)
ldapcn.Start()
return &ldapConn{Conn: ldapcn, BaseDN: c.BaseDN, RoleBaseDN: c.RoleBaseDN}, nil
}
type ldapConn struct {
*ldap.Conn
BaseDN string
RoleBaseDN string
}
func (c *ldapConn) Bind(bindDN, password string) error {
err := c.Conn.Bind(bindDN, password)
if ldapErr, ok := err.(*ldap.Error); ok && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials {
return errInvalidCredentials
}
return err
}
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][]string, error) {
query := fmt.Sprintf("(|"+
"(&(|(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][]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][]string
for _, v := range res.Entries {
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
}
entries = append(entries, entry)
}
return entries, nil
}

View File

@@ -0,0 +1,32 @@
package claims
import (
"oc-auth/conf"
"cloud.o-forge.io/core/oc-lib/models/peer"
)
// Tokenizer interface
type ClaimService interface {
AddClaimsToToken(clientID string, userId string, peer *peer.Peer) Claims
DecodeClaimsInToken(host string, method string, forward string, sessionClaims Claims, publicKey string, external bool) (bool, error)
}
// SessionClaims struct
type SessionClaims struct {
AccessToken map[string]interface{} `json:"access_token"`
IDToken map[string]interface{} `json:"id_token"`
}
// Claims struct
type Claims struct {
Session SessionClaims `json:"session"`
}
var t = map[string]ClaimService{
"hydra": HydraClaims{},
}
func GetClaims() ClaimService {
return t[conf.GetConfig().Auth]
}

View File

@@ -0,0 +1,129 @@
package claims
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"errors"
"log"
)
func SignDefault(plaintext, privateKey []byte) (signature string, err error) {
client, err := New(privateKey, nil)
if err != nil {
log.Println(err)
return
}
signatureByte, err := client.Sign(plaintext)
if err != nil {
log.Println(err)
return
}
signature = base64.StdEncoding.EncodeToString(signatureByte)
return
}
func VerifyDefault(plaintext, publicKey []byte, signature string) (err error) {
publicKeys := make(map[string][]byte)
publicKeys["default"] = publicKey
client, err := New(nil, publicKeys)
if err != nil {
log.Println(err)
return
}
signatureByte, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
log.Println(err)
return
}
err = client.Verify(plaintext, signatureByte, "default")
if err != nil {
log.Println(err)
return
}
return
}
func (c *Client) Sign(plaintext []byte) (signature []byte, err error) {
var opts rsa.PSSOptions
opts.SaltLength = rsa.PSSSaltLengthAuto
newhash := crypto.SHA256
pssh := newhash.New()
pssh.Write(plaintext)
hashed := pssh.Sum(nil)
signature, err = rsa.SignPSS(
rand.Reader,
c.PrivateKey,
newhash,
hashed,
&opts,
)
return
}
func (c *Client) Verify(plaintext, signature []byte, target string) (err error) {
var opts rsa.PSSOptions
opts.SaltLength = rsa.PSSSaltLengthAuto
newhash := crypto.SHA256
pssh := newhash.New()
pssh.Write(plaintext)
hashed := pssh.Sum(nil)
err = rsa.VerifyPSS(
c.PublicKeys[target],
newhash,
hashed,
signature,
&opts,
)
return
}
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
type Client struct {
PrivateKey *rsa.PrivateKey
PublicKeys map[string]*rsa.PublicKey
}
func New(privateKey []byte, publicKeys map[string][]byte) (client *Client, err error) {
client = &Client{}
if privateKey != nil {
validPrivateKey, errPrivate := x509.ParsePKCS1PrivateKey(privateKey)
if errPrivate != nil {
err = errPrivate
log.Println(err)
return
}
client.PrivateKey = validPrivateKey
}
if publicKeys != nil {
validPublicKeysMap := make(map[string]*rsa.PublicKey)
for k, v := range publicKeys {
validPublicKey, errPublic := x509.ParsePKCS1PublicKey(v)
if errPublic != nil {
err = errPublic
log.Println(err)
return
}
if validPublicKey == nil {
err = errors.New("Invalid Public Key Type")
log.Println(err)
return
}
validPublicKeysMap[k] = validPublicKey
}
client.PublicKeys = validPublicKeysMap
}
return
}

View File

@@ -0,0 +1,162 @@
package claims
import (
"crypto/sha256"
"encoding/pem"
"errors"
"fmt"
"oc-auth/conf"
"oc-auth/infrastructure/perms_connectors"
"oc-auth/infrastructure/utils"
"os"
"strings"
"time"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/tools"
)
type HydraClaims struct{}
func (h HydraClaims) generateKey(relation string, path string) (string, error) {
method, err := utils.ExtractMethod(relation, true)
if err != nil {
return "", err
}
p := strings.ReplaceAll(strings.ToUpper(path), "/", "_")
return strings.ToUpper(method.String()) + "_" + strings.ReplaceAll(p, ":", ""), nil
}
// decode key expect to extract method and path from key
func (h HydraClaims) decodeKey(key string, external bool) (tools.METHOD, string, error) {
s := strings.Split(key, "_")
if len(s) < 2 {
return tools.GET, "", errors.New("invalid key")
}
if strings.Contains(strings.ToUpper(s[0]), "INTERNAL") && external {
return tools.GET, "", errors.New("external ask for internal key")
}
meth, err := utils.ExtractMethod(s[0], false)
if err != nil {
return meth, "", err
}
p := strings.ReplaceAll(strings.ToUpper(s[1]), "_", "/")
return meth, p, nil
}
func (h HydraClaims) DecodeSignature(host string, signature string, publicKey string) (bool, error) {
hashed := sha256.Sum256([]byte(host))
spkiBlock, _ := pem.Decode([]byte(publicKey)) // get public key into a variable
err := VerifyDefault(hashed[:], spkiBlock.Bytes, signature)
if err != nil {
return false, err
}
return true, nil
}
func (h HydraClaims) encodeSignature(host string) (string, error) {
hashed := sha256.Sum256([]byte(host))
// READ FILE TO GET PRIVATE KEY FROM PVK PEM PATH
content, err := os.ReadFile(conf.GetConfig().PrivateKeyPath)
if err != nil {
return "", err
}
privateKey := string(content)
spkiBlock, _ := pem.Decode([]byte(privateKey))
return SignDefault(hashed[:], spkiBlock.Bytes)
}
func (h HydraClaims) clearBlank(path []string) []string {
// clear blank
newPath := []string{}
for _, p := range path {
if p != "" {
newPath = append(newPath, p)
}
}
return newPath
}
func (a HydraClaims) CheckExpiry(exp int64) bool {
now := time.Now().UTC().Unix()
return now <= exp
}
func (h HydraClaims) DecodeClaimsInToken(host string, method string, forward string, sessionClaims Claims, publicKey string, external bool) (bool, error) {
idTokenClaims := sessionClaims.Session.IDToken
if idTokenClaims["signature"] == nil {
return false, errors.New("no signature found")
}
signature := idTokenClaims["signature"].(string)
if ok, err := h.DecodeSignature(host, signature, publicKey); !ok {
return false, err
}
claims := sessionClaims.Session.AccessToken
path := strings.ReplaceAll(forward, "http://"+host, "")
splittedPath := h.clearBlank(strings.Split(path, "/"))
if _, ok := claims["exp"].(float64); !ok || !h.CheckExpiry(int64(claims["exp"].(float64))) {
return false, errors.New("token is expired")
}
for m, p := range claims {
match := true
splittedP := h.clearBlank(strings.Split(p.(string), "/"))
if len(splittedP) != len(splittedPath) {
continue
}
for i, v := range splittedP {
if strings.Contains(v, ":") { // is a param
continue
} else if v != splittedPath[i] {
match = false
break
}
}
if match {
meth, _, err := h.decodeKey(m, external)
if err != nil {
continue
}
perm := perms_connectors.Permission{
Relation: "permits" + strings.ToUpper(meth.String()),
Object: p.(string),
}
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(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 {
continue
}
claims.Session.AccessToken[key] = perm.Subject
}
sign, err := h.encodeSignature(p.Url)
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
}

View File

@@ -0,0 +1,19 @@
package infrastructure
import (
auth_connectors "oc-auth/infrastructure/auth_connector"
"oc-auth/infrastructure/claims"
"oc-auth/infrastructure/perms_connectors"
)
func GetAuthConnector() auth_connectors.AuthConnector {
return auth_connectors.GetAuthConnector()
}
func GetPermissionConnector(client string) perms_connectors.PermConnector {
return perms_connectors.GetPermissionConnector(client)
}
func GetClaims() claims.ClaimService {
return claims.GetClaims()
}

View File

@@ -0,0 +1,404 @@
package perms_connectors
import (
"encoding/json"
"errors"
"fmt"
"oc-auth/conf"
"oc-auth/infrastructure/utils"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/tools"
)
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-realm"
}
func (f KetoConnector) permToQuery(perm Permission, permDependancies *Permission) string {
n := "?namespace=" + f.namespace()
if perm.Object != "" {
n += "&object=" + perm.Object
}
if perm.Subject != "" {
n += "&subject_id=" + perm.Subject
}
if perm.Relation != "" {
n += "&relation=" + perm.Relation
}
if permDependancies != nil {
n += "&subject_set.namespace=" + perm.Namespace()
if permDependancies.Object != "" {
n += "&subject_set.object=" + permDependancies.Object
}
if permDependancies.Subject != "" {
n += "&subject_set.subject_id=" + permDependancies.Subject
}
if permDependancies.Relation != "" {
n += "&subject_set.relation=" + permDependancies.Relation
}
}
return n
}
func (k KetoConnector) Status() tools.State {
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{})
var responseBody map[string]interface{}
host := conf.GetConfig().PermissionConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorPort)
resp, err := caller.CallGet("http://"+host+":"+port, "/health/ready")
if err != nil {
return tools.DEAD
}
err = json.Unmarshal(resp, &responseBody)
if err != nil || responseBody["status"] != "ok" {
return tools.DEAD
}
return tools.ALIVE
}
func (k KetoConnector) CheckPermission(perm Permission, permDependancies *Permission, internal bool) bool {
if (perm.Object == k.scope() || perm.Subject == k.scope()) && !internal {
log := oclib.GetLogger()
log.Error().Msg("Permission denied : Ask illegal permission")
return false
}
perms, err := k.GetPermission(perm.Object, perm.Relation)
if err != nil {
log := oclib.GetLogger()
log.Error().Msg(err.Error())
return false
}
return len(perms) > 0
}
func (k KetoConnector) deletes(object string, relation string, subject string, relation2 string) (string, int, error) {
k.deleteRelationShip(object, relation, subject, nil)
_, code, err := k.deleteRelationShip(subject, relation2, k.scope(), nil)
if err != nil {
return "", code, err
}
return subject, 200, nil
}
func (k KetoConnector) DeleteRole(roleID string) (string, int, error) {
return k.deletes("", "member", roleID, "is")
}
func (k KetoConnector) DeleteGroup(groupID string) (string, int, error) {
return k.deletes("", "groups", groupID, "groupin")
}
func (k KetoConnector) DeletePermission(permID string, relation string, internal bool) (string, int, error) {
meth, err := utils.ExtractMethod(relation, internal)
if err != nil {
for _, method := range []tools.METHOD{tools.GET, tools.PUT, tools.POST, tools.DELETE} {
k.DeletePermission("", method.String(), internal)
}
return "", 200, err
}
return k.deletes("", "groups", permID, "permits"+meth.String())
}
func (k KetoConnector) CreateRole(roleID string) (string, int, error) {
return k.creates(roleID, "is", k.scope())
}
func (k KetoConnector) CreateGroup(groupID string) (string, int, error) {
return k.creates(groupID, "groupin", k.scope())
}
func (k KetoConnector) CreatePermission(permID string, relation string, internal bool) (string, int, error) {
meth, err := utils.ExtractMethod(relation, internal)
if err != nil {
return "", 422, err
}
k.BindPermission("admin", permID, "permits"+meth.String())
return k.creates(permID, "permits"+meth.String(), k.scope())
}
func (k KetoConnector) creates(object string, relation string, subject string) (string, int, error) {
p, code, err := k.createRelationShip(object, relation, subject, nil)
if err != nil {
return "", code, err
}
return p.Object, 200, nil
}
func (k KetoConnector) GetRole(roleID string) ([]string, error) {
return k.gets(roleID, "is", k.scope())
}
func (k KetoConnector) GetGroup(groupID string) ([]string, error) {
return k.gets(groupID, "groupin", k.scope())
}
func (k KetoConnector) GetRoleByUser(userID string) ([]string, error) {
return k.gets("", "member", userID)
}
func (k KetoConnector) GetGroupByUser(userID string) ([]string, error) {
return k.gets("", "groups", userID)
}
func (k KetoConnector) gets(object string, relation string, subject string) ([]string, error) {
arr := []string{}
objs, err := k.get(object, relation, subject)
if err != nil {
return arr, err
}
for _, obj := range objs {
arr = append(arr, obj.Object)
}
return arr, nil
}
func (k KetoConnector) GetPermission(permID string, relation string) ([]Permission, error) {
meth, err := utils.ExtractMethod(relation, true)
if err != nil {
p := []Permission{}
for _, method := range []tools.METHOD{tools.GET, tools.PUT, tools.POST, tools.DELETE,
tools.STRICT_INTERNAL_DELETE, tools.STRICT_INTERNAL_GET, tools.STRICT_INTERNAL_POST, tools.STRICT_INTERNAL_PUT} {
perms, err := k.get(permID, "permits"+method.String(), k.scope())
if err == nil && len(perms) > 0 {
p = append(p, perms...)
}
}
return p, nil
}
return k.get(permID, "permits"+meth.String(), k.scope())
}
func (k KetoConnector) GetPermissionByRole(roleID string) ([]Permission, error) {
p := []Permission{}
for _, method := range []tools.METHOD{tools.GET, tools.PUT, tools.POST, tools.DELETE,
tools.STRICT_INTERNAL_DELETE, tools.STRICT_INTERNAL_GET, tools.STRICT_INTERNAL_POST, tools.STRICT_INTERNAL_PUT} {
perms, err := k.get(roleID, "permits"+method.String(), "")
if err == nil && len(perms) > 0 {
p = append(p, perms...)
}
}
return p, nil
}
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
}
p := []Permission{}
meths := []tools.METHOD{tools.GET, tools.PUT, tools.POST, tools.DELETE}
if internal {
meths = append(meths, []tools.METHOD{tools.STRICT_INTERNAL_DELETE, tools.STRICT_INTERNAL_GET, tools.STRICT_INTERNAL_POST, tools.STRICT_INTERNAL_PUT}...)
}
for _, role := range roles {
for _, method := range meths {
perms, err := k.get(role.Object, "permits"+method.String(), "")
if err == nil && len(perms) > 0 {
p = append(p, perms...)
}
}
}
return p, nil
}
func (k KetoConnector) get(object string, relation string, subject string) ([]Permission, error) {
t := []Permission{}
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{})
host := conf.GetConfig().PermissionConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorPort)
resp, err := caller.CallGet("http://"+host+":"+port, "/relation-tuples"+k.permToQuery(
Permission{Object: object, Relation: relation, Subject: subject}, nil))
if err != nil {
return t, err
}
var data map[string]interface{}
err = json.Unmarshal(resp, &data)
if err != nil {
return t, err
}
if data["relation_tuples"] != nil {
for _, v := range data["relation_tuples"].([]interface{}) {
t = append(t, Permission{
Relation: v.(map[string]interface{})["relation"].(string),
Subject: v.(map[string]interface{})["subject_id"].(string),
Object: v.(map[string]interface{})["object"].(string),
})
}
}
return t, nil
}
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
}
return object, 200, nil
}
func (k KetoConnector) BindRole(userID string, roleID string) (string, int, error) {
fmt.Println("BIND ROLE", userID, roleID)
return k.binds(userID, "member", roleID)
}
func (k KetoConnector) BindGroup(userID string, groupID string) (string, int, error) {
return k.binds(userID, "groups", groupID)
}
func (k KetoConnector) BindPermission(roleID string, permID string, relation string) (*Permission, int, error) {
perms, err := k.GetPermission(permID, relation)
if err != nil || len(perms) != 1 {
count := 0
for _, p := range perms {
if p.Relation == relation {
count++
}
}
if count == 0 {
return nil, 404, errors.New("Permission not found")
} else if count > 1 {
return nil, 409, errors.New("Multiple permission found")
}
}
_, code, err := k.createRelationShip(roleID, relation, permID, nil)
if err != nil {
return nil, code, err
}
return &Permission{
Object: roleID,
Relation: relation,
Subject: permID,
}, 200, nil
}
func (k KetoConnector) unbinds(subject string, relation string, object string) (string, int, error) {
_, code, err := k.deleteRelationShip(object, relation, subject, nil)
if err != nil {
return object, code, err
}
return object, 200, nil
}
func (k KetoConnector) UnBindRole(userID string, roleID string) (string, int, error) {
return k.unbinds(userID, "member", roleID)
}
func (k KetoConnector) UnBindGroup(userID string, groupID string) (string, int, error) {
return k.unbinds(userID, "groups", groupID)
}
func (k KetoConnector) UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error) {
meth, err := utils.ExtractMethod(relation, false)
if err != nil {
return nil, 422, err
}
perms, err := k.GetPermission(permID, meth.String())
if err != nil || len(perms) != 1 {
count := 0
for _, p := range perms {
if p.Relation == relation {
count++
}
}
if count == 0 {
return nil, 404, errors.New("Permission not found")
} else if count > 1 {
return nil, 409, errors.New("Multiple permission found")
}
}
_, code, err := k.deleteRelationShip(roleID, perms[0].Relation, permID, nil)
if err != nil {
return nil, code, err
}
return &Permission{
Object: roleID,
Relation: perms[0].Relation,
Subject: permID,
}, 200, nil
}
func (k KetoConnector) createRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) {
exist, err := k.get(object, relation, subject)
if err == nil && len(exist) > 0 {
return nil, 409, errors.New("Relation already exist")
}
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{})
body := map[string]interface{}{"namespace": k.namespace(), "object": object, "relation": relation, "subject_id": subject}
if subPerm != nil {
s, code, err := k.createRelationShip(subPerm.Object, subPerm.Relation, subPerm.Subject, nil)
if err != nil {
return nil, code, err
}
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)
b, err := caller.CallPut("http://"+host+":"+port, "/relation-tuples", body)
if err != nil {
log := oclib.GetLogger()
log.Error().Msg(err.Error())
return nil, 500, err
}
var data map[string]interface{}
err = json.Unmarshal(b, &data)
if err != nil {
log := oclib.GetLogger()
log.Error().Msg(err.Error())
return nil, 500, err
}
perm := &Permission{
Object: data["object"].(string),
Relation: data["relation"].(string),
Subject: data["subject_id"].(string),
}
if data["subject_set"] != nil {
sub := data["subject_set"].(map[string]interface{})
perm.SubPermission = &Permission{
Object: sub["object"].(string),
Relation: sub["relation"].(string),
Subject: sub["subject_id"].(string),
}
}
return perm, 200, nil
}
func (k KetoConnector) deleteRelationShip(object string, relation string, subject string, subPerm *Permission) (*Permission, int, error) {
exist, err := k.get(object, relation, subject)
if err == nil && len(exist) == 0 {
return nil, 409, errors.New("Relation does not exist")
}
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{})
n := k.permToQuery(Permission{Object: object, Relation: relation, Subject: subject}, subPerm)
host := conf.GetConfig().PermissionConnectorHost
port := fmt.Sprintf("%v", conf.GetConfig().PermissionConnectorAdminPort)
b, err := caller.CallDelete("http://"+host+":"+port, "/relation-tuples"+n)
if err != nil {
log := oclib.GetLogger()
log.Error().Msg(err.Error())
return nil, 500, err
}
var data map[string]interface{}
err = json.Unmarshal(b, &data)
if err == nil && data["code"].(int) > 300 {
return nil, data["code"].(int), errors.New("Error while deleting relation")
}
return &Permission{
Object: object,
Relation: relation,
Subject: subject,
SubPermission: subPerm,
}, 200, nil
}

View File

@@ -0,0 +1,59 @@
package perms_connectors
import (
"oc-auth/conf"
"cloud.o-forge.io/core/oc-lib/tools"
)
type Permission struct {
Object string `json:"object,omitempty"`
Relation string `json:"relation,omitempty"`
Subject string `json:"subject,omitempty"`
SubPermission *Permission `json:"sub_perm,omitempty"`
}
func (p Permission) Namespace() string {
return "open-cloud"
}
func (k Permission) Scope() string {
return "oc-auth"
}
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)
BindPermission(roleID string, permID string, relation string) (*Permission, int, error)
UnBindRole(userID string, roleID string) (string, int, error)
UnBindGroup(userID string, groupID string) (string, int, error)
UnBindPermission(roleID string, permID string, relation string) (*Permission, int, error)
CreateRole(roleID string) (string, int, error)
CreateGroup(groupID string) (string, int, error)
CreatePermission(permID string, relation string, internal bool) (string, int, error)
DeleteRole(roleID string) (string, int, error)
DeleteGroup(groupID string) (string, int, error)
DeletePermission(permID string, relation string, internal bool) (string, int, error)
GetRoleByUser(userID string) ([]string, error)
GetGroupByUser(userID string) ([]string, error)
GetPermissionByRole(roleID string) ([]Permission, error)
GetPermissionByUser(userID string, internal bool) ([]Permission, error)
GetRole(roleID string) ([]string, error)
GetGroup(groupID string) ([]string, error)
GetPermission(permID string, relation string) ([]Permission, error)
}
var c = map[string]PermConnector{
"keto": KetoConnector{},
}
func GetPermissionConnector(scope string) PermConnector {
return c[conf.GetConfig().PermissionConnectorHost]
}

View File

@@ -0,0 +1,21 @@
package utils
import (
"errors"
"strings"
"cloud.o-forge.io/core/oc-lib/tools"
)
func ExtractMethod(relation string, internal bool) (tools.METHOD, error) {
meths := []tools.METHOD{tools.GET, tools.PUT, tools.POST, tools.DELETE}
if internal {
meths = append(meths, []tools.METHOD{tools.STRICT_INTERNAL_GET, tools.STRICT_INTERNAL_POST, tools.STRICT_INTERNAL_POST, tools.STRICT_INTERNAL_DELETE}...)
}
for _, method := range meths {
if (!internal && strings.Contains(strings.ToUpper(relation), strings.ToUpper(method.String()))) || (internal && strings.ToUpper(relation) == strings.ToUpper(method.String())) {
return method, nil
}
}
return tools.GET, errors.New("method not found")
}

21
keto/docker-compose.yml Normal file
View File

@@ -0,0 +1,21 @@
version: '3.4'
services:
keto:
image: oryd/keto:v0.7.0-alpha.1-sqlite
ports:
- "4466:4466"
- "4467:4467"
command: serve -c /home/ory/keto.yml
restart: on-failure
volumes:
- type: bind
source: .
target: /home/ory
container_name: keto
networks:
- catalog
networks:
catalog:
external: true

18
keto/keto.yml Normal file
View File

@@ -0,0 +1,18 @@
version: v0.6.0-alpha.1
log:
level: debug
namespaces:
- id: 0
name: open-cloud
dsn: memory
serve:
read:
host: 0.0.0.0
port: 4466
write:
host: 0.0.0.0
port: 4467

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

View File

@@ -0,0 +1,79 @@
version: "3"
services:
hydra-client:
image: oryd/hydra:v2.2.0
container_name: hydra-client
environment:
HYDRA_ADMIN_URL: http://hydra:4445
ORY_SDK_URL: http://hydra: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
healthcheck:
test: ["CMD", "curl", "-f", "http://hydra:4445"]
interval: 10s
timeout: 10s
retries: 10
hydra:
container_name: hydra
image: oryd/hydra:v2.2.0
environment:
SECRETS_SYSTEM: oc-auth-got-secret
LOG_LEAK_SENSITIVE_VALUES: true
# OAUTH2_TOKEN_HOOK_URL: http://oc-auth:8080/oc/claims
URLS_SELF_ISSUER: http://hydra:4444
URLS_SELF_PUBLIC: http://hydra: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:
- "4444:4444"
- "4445:4445"
deploy:
restart_policy:
condition: on-failure
ldap:
image: pgarrett/ldap-alpine
container_name: ldap
volumes:
- "./ldap.ldif:/ldif/ldap.ldif"
networks:
- hydra-net
- catalog
ports:
- "390: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

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

@@ -0,0 +1,24 @@
dn: uid=admin,ou=Users,dc=example,dc=com
objectClass: inetOrgPerson
cn: Admin
sn: Istrator
uid: admin
userPassword: admin
mail: admin@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=admin,ou=Users,dc=example,dc=com

142
main.go
View File

@@ -1,41 +1,145 @@
package main package main
import ( import (
"context"
"errors"
"fmt"
"oc-auth/conf" "oc-auth/conf"
"oc-auth/infrastructure"
auth_connectors "oc-auth/infrastructure/auth_connector"
_ "oc-auth/routers" _ "oc-auth/routers"
"os"
"strconv"
"strings"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
peer "cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
) )
const appname = "oc-auth" const appname = "oc-auth"
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.
func main() { func main() {
// Init the oc-lib // Init the oc-lib
oclib.Init(appname) oclib.Init(appname)
// Load the right config file // Load the right config file
o := oclib.GetConfLoader() o := oclib.GetConfLoader()
conf.GetConfig().NatsUrl = o.GetStringDefault("natsurl", "http://localhost:4080") conf.GetConfig().AdminRole = o.GetStringDefault("ADMIN_ROLE", "admin")
conf.GetConfig().NatsLogin = o.GetStringDefault("natslogin", "admin") conf.GetConfig().PublicKeyPath = o.GetStringDefault("PUBLIC_KEY_PATH", "./pem/public.pem")
conf.GetConfig().NatsPassword = o.GetStringDefault("natspassword", "admin") conf.GetConfig().PrivateKeyPath = o.GetStringDefault("PRIVATE_KEY_PATH", "./pem/private.pem")
conf.GetConfig().OidcUrl = o.GetStringDefault("oidcurl", "admin") conf.GetConfig().ClientSecret = o.GetStringDefault("CLIENT_SECRET", "oc-auth-got-secret")
// feed the library with the loaded config conf.GetConfig().Auth = o.GetStringDefault("AUTH", "hydra")
oclib.SetConfig( conf.GetConfig().AuthConnectorHost = o.GetStringDefault("AUTH_CONNECTOR_HOST", "localhost")
o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"), conf.GetConfig().AuthConnectorPort = o.GetIntDefault("AUTH_CONNECTOR_PORT", 4444)
o.GetStringDefault("MONGO_DATABASE", "DC_myDC"), conf.GetConfig().AuthConnectorAdminPort = o.GetIntDefault("AUTH_CONNECTOR_ADMIN_PORT", 4445)
"", conf.GetConfig().PermissionConnectorHost = o.GetStringDefault("PERMISSION_CONNECTOR_HOST", "keto")
o.GetStringDefault("lokiurl", ""), conf.GetConfig().PermissionConnectorPort = o.GetIntDefault("PERMISSION_CONNECTOR_PORT", 4466)
o.GetStringDefault("loglevel", "info"), conf.GetConfig().PermissionConnectorAdminPort = o.GetIntDefault("PERMISSION_CONNECTOR_ADMIN_PORT", 4467)
)
// Beego init
beego.BConfig.AppName = appname
beego.BConfig.Listen.HTTPPort = o.GetIntDefault("port", 8080)
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
// 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")
conf.GetConfig().LDAPBaseDN = o.GetStringDefault("LDAP_BASEDN", "dc=example,dc=com")
conf.GetConfig().LDAPRoleBaseDN = o.GetStringDefault("LDAP_ROLE_BASEDN", "ou=AppRoles,dc=example,dc=com")
err := generateSelfPeer()
if err != nil {
panic(err)
}
generateRole()
discovery()
beego.Run() beego.Run()
}
func generateRole() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
// if from ldap, create roles from ldap
if conf.GetConfig().SourceMode == "ldap" {
ldap := auth_connectors.New()
roles, err := ldap.GetRoles(context.Background())
if err != nil {
panic(err)
}
fmt.Println("ROLE", roles)
for _, role := range roles {
for r, m := range role.Members {
infrastructure.GetPermissionConnector("").CreateRole(r)
for _, p := range m {
infrastructure.GetPermissionConnector("").BindRole(r, p)
}
}
}
}
}
func generateSelfPeer() error {
// TODO check if files at private & public path are set
// check if files at private & public path are set
if _, err := os.Stat(conf.GetConfig().PrivateKeyPath); errors.Is(err, os.ErrNotExist) {
return errors.New("private key path does not exist")
}
if _, err := os.Stat(conf.GetConfig().PublicKeyPath); errors.Is(err, os.ErrNotExist) {
return errors.New("public key path does not exist")
}
// check if peer already exists
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 {
return err
}
file = string(f)
if len(p.Data) > 0 {
// check public key with the one in the database
// compare the public key from file with the one in the database
if !strings.Contains(file, p.Data[0].(*peer.Peer).PublicKey) {
return errors.New("public key is different from the one in the database")
}
return nil
}
// create a new peer
o := oclib.GetConfLoader()
peer := &peer.Peer{
Url: o.GetStringDefault("HOSTNAME", "http://localhost"),
AbstractObject: utils.AbstractObject{
Name: o.GetStringDefault("NAME", "local"),
},
PublicKey: file,
State: peer.SELF,
}
data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", []string{}, nil).StoreOne(peer.Serialize(peer))
if data.Err != "" {
return errors.New(data.Err)
}
return nil
}
func discovery() {
api := tools.API{}
conn := infrastructure.GetPermissionConnector("")
conn.CreateRole(conf.GetConfig().AdminRole)
conn.BindRole(conf.GetConfig().AdminRole, "admin")
addPermissions := func(m map[string]interface{}) {
for k, v := range m {
for _, p := range v.([]interface{}) {
conn.CreatePermission(k, p.(string), true)
}
}
}
api.ListenRouter(addPermissions)
tools.NewNATSCaller().SetNATSPub("api", tools.DISCOVERY, map[string]interface{}{})
} }

View File

@@ -1,10 +0,0 @@
package models
// OpenId application
type Application struct {
ClientId string `json:"client_id,omitempty"`
ClientName string `json:"client_name,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
AccessToken string `json:"access_token,omitempty"`
CallbackUrl string `json:"callback_url,omitempty"`
}

View File

@@ -1,12 +0,0 @@
package models
import (
"fmt"
"oc-auth/conf"
)
func init() {
c := conf.GetConfig()
// configure storage
fmt.Println(c.NatsUrl)
}

View File

@@ -1,137 +0,0 @@
package models
import (
"context"
"fmt"
"os"
client "github.com/ory/hydra-client-go"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
func CreateClient(clientId string, clientName string, clientSecret string) (string, string, error) {
tokenAuthMethod := "client_secret_post"
oAuth2Client := *client.NewOAuth2Client() // OAuth2Client |
oAuth2Client.SetClientId(clientId)
oAuth2Client.SetClientName(clientName)
oAuth2Client.SetClientSecret(clientSecret)
oAuth2Client.SetGrantTypes([]string{"client_credentials"})
oAuth2Client.TokenEndpointAuthMethod = &tokenAuthMethod
config := client.NewConfiguration()
config.Servers = client.ServerConfigurations{{URL: "http://127.0.0.1:4445"}}
client := client.NewAPIClient(config)
resp, _, err := client.AdminApi.CreateOAuth2Client(context.Background()).OAuth2Client(oAuth2Client).Execute()
if err != nil {
return "", "", err
}
return resp.GetClientId(), resp.GetClientSecret(), nil
}
func GetAccessToken(clientID, clientSecret string) (string, error) {
config := clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
TokenURL: "http://127.0.0.1:4444/oauth2/token",
AuthStyle: oauth2.AuthStyleInParams,
}
token, err := config.Token(context.Background())
if err != nil {
fmt.Println("Error obtaining token:", err)
return "", err
}
return token.AccessToken, nil
}
func ListClients() {
configuration := client.NewConfiguration()
configuration.Servers = []client.ServerConfiguration{
{
URL: "http://localhost:4445", // Public API URL
},
}
apiClient := client.NewAPIClient(configuration)
limit := int64(20)
offset := int64(0)
clients, r, err := apiClient.AdminApi.ListOAuth2Clients(context.Background()).Limit(limit).Offset(offset).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `OAuth2Api.ListOAuth2Clients``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
for i, c := range clients {
fmt.Fprintf(os.Stdout, " %d : %s %s %s\n", i, *c.ClientId, c.GetClientName(), c.GetClientSecret())
}
fmt.Fprintf(os.Stdout, "We have %d clients\n", len(clients))
}
func DeleteClient(clientId string) {
configuration := client.NewConfiguration()
configuration.Servers = []client.ServerConfiguration{
{
URL: "http://localhost:4445", // Public API URL
},
}
apiClient := client.NewAPIClient(configuration)
r, err := apiClient.AdminApi.DeleteOAuth2Client(context.Background(), clientId).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `OAuth2Api.DeleteOAuth2Client``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
}
func DeleteClients() {
configuration := client.NewConfiguration()
configuration.Servers = []client.ServerConfiguration{
{
URL: "http://localhost:4445", // Public API URL
},
}
apiClient := client.NewAPIClient(configuration)
limit := int64(20)
offset := int64(0)
clients, r, err := apiClient.AdminApi.ListOAuth2Clients(context.Background()).Limit(limit).Offset(offset).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `OAuth2Api.ListOAuth2Clients``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
for _, c := range clients {
fmt.Fprintf(os.Stdout, " Deleting : %s %s %s\n", c.GetClientId(), c.GetClientName(), c.GetClientSecret())
r, err := apiClient.AdminApi.DeleteOAuth2Client(context.Background(), c.GetClientId()).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `OAuth2Api.DeleteOAuth2Client``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
fmt.Fprintf(os.Stdout, " Deleted: %s\n", c.GetClientId())
}
fmt.Fprintf(os.Stdout, "We deleted %d clients\n", len(clients))
}
func CreateCodeClient(clientId string) (string, string, error) {
config := client.NewConfiguration()
config.Servers = client.ServerConfigurations{{URL: "http://127.0.0.1:4445"}}
tokenAuthMethod := "client_secret_post"
oAuth2Client := *client.NewOAuth2Client() // OAuth2Client |
oAuth2Client.SetClientId(clientId + "_api")
oAuth2Client.SetGrantTypes([]string{"authorization_code", "refresh_token"})
oAuth2Client.SetResponseTypes([]string{"code", "id_token"})
oAuth2Client.SetScope("openid offline")
oAuth2Client.SetRedirectUris([]string{"http://127.0.0.1:5555/callback"})
oAuth2Client.TokenEndpointAuthMethod = &tokenAuthMethod
client := client.NewAPIClient(config)
resp, _, err := client.AdminApi.CreateOAuth2Client(context.Background()).OAuth2Client(oAuth2Client).Execute()
if err != nil {
return "", "", err
}
return resp.GetClientId(), resp.GetClientSecret(), nil
}

BIN
oc-auth

Binary file not shown.

51
pem/private.pem Normal file
View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAw2pdG6wMtuLcP0+k1LFvIb0DQo/oHW2uNJaEJK74plXqp4zt
z2dRb+RQHFLeLuqk4i/zc3b4K3fKPXSlwnVPJCwzPrnyT8jYGOZVlWlETiV9xeJh
u6s/Bh6g1PWz75XjjwV50iv/CEiLNBT23f/3J44wrQzygqNQCiQSALdxWLAEl4l5
kHSa9oMyV70/Uql94/ayMARZsHgp9ZvqQKbkZPw6yzVMfCBxQozlNlo315OHevud
hnhpDRjN5I7zWmqYt6rbXJJC7Y3Izdvzn7QI88RqjSRST5I/7Kz3ndCqrOnI+OQU
E5NTREyQebphvQfTDTKlRPXkdyktdK2DH28Zj6ZF3yjQvN35Q4zhOzlq77dO5Ihh
opI7ct8dZH1T1nYkvdyCA/EVMtQsASmBOitH0Y0ACoXQK5Kb6nm/TcM/9ZSJUNiE
Muy5gBZ3YKE9oa4cpTpPXwcA+S/cU7HPNnQAsvD3iJi8GTW9uJs84pn4/WhpQqmX
d4rvhKWECCN3fHy01fUs/U0PaSj2jDY/kQVeXoikNMzPUjdZd9m816TIBh3v3aVX
CH/0iTHHAxctvDgMRb2fpvRJ/wwnYjFG9RpamVFDMvC9NffuYzWAA9IRIY4cqger
fHrVZ2HHiPTDDvDAIsvImXZc/h7mXN6m3RCQ4Qywy993wd9gUdgg/qnynHcCAwEA
AQKCAgBSRc29gMo5lXm1DgsPoURwp+tfcsb+3NajPVuVNjVpknKg6CyXTaBzw2QX
CKyShCe3MwkEa+pAIsb66MmA/XK8f/9zQUZLYPvaP994cEFZxV8WmSEcqhR2tx5v
iqKfFDQiWuPXIL7W9fPlkY3+GW4tMSg9M15GsgtYuab6tkD6XeERC8gqkW1MrB/d
4MdwPfvKpmqO3MYGDhFcXrBZV+qAudDnDSGOgPouUrOOFp28HVjE5nqDyt4vrWnB
+I1sW8TATybb6phS+4a3ZQtFCb9bIi7aDZi595ECTDBUOS4ibqs2XpA1TamY78ND
/Lx5oXmx7Mi4J+5wXN3Oad7ytQvFOdOttILNpAMFbLU5yKsxf+EDjCBcR5faNAtu
wAMH9TkzFpmhp5NBwQdAkCFnhyla363ljmlGAWPuG/D9bVZ0qtBf3NAF4hEJ/L4n
vYQFErd64tPasAgWPow2LLvqi3jT232aDgahj1lX1enHoR6TbQE4r3zy8DeA7i0C
hA8GFAvMsqIcee9Xi4yD8QFEH0PrJh6zP1PWKXR4AQVExnFGanz0s6O1GRqLPrIK
31NiRTwIVp0xbet9/GsJxWxqrTKPqd5yJvUGG3A4EL8e3RdWRM5vTHbNCVGi9LhS
3OIXV9/YOQJtWxzWUjFN3HpaNuWHVSR149GAYpTxeh+/ZGoAQQKCAQEA9RzG0LrB
/onSnzfAXD5IwGW/jSb4e8VLCZ/7W0CiD1ht23Q1pAR4bofSYXdx/rhVhRty+38q
GzugBPcejGeGYyKIViO1e8Psrd6dydYoaA9fX3YzcZ0onk9t+tYNxVhstolfIGgc
KW1qwDNrV1PP0N/oQU4QPxLmwcf/BSSl3sf53mVMzcnHkjCF5SgEN1rtIAOUZaM8
B4urSSMZMLpwmhXF9rAUPXWgLS+0fkrQzoaBS2ZfqEcFNs1ln8sTBeoXAz6kyZem
0To1oYhTeN+Tg11quWhJKiTMYmSGT3B7nVt25n0BypDktfaUU+LMY9/BewWwE6xx
iX97bldgv6y4uwKCAQEAzBh5IYa09uJTwrxY0IKVfkXRp2S+8fqhJ5qhEPykCF+4
JJBKPp7IA7rhcf5tw3NVpa/YE1h2H1P5Z4EQkzgETExVotBlOYYNmkBkd9etURGs
omAPHprnvT7q+dR/rc8/lQt3Xzvej4P4zSRbnzg/q2K0I1qs3vOwnZyO3GXk6eni
Q59k0y8zosKdde7m1YJ+KAj5MZc3PeHTfbLaLcfXnTSUX6xwnX+Js8kr2PVP+wSj
sExakP6ieNu/ZzFcYv6aiPgyXIWO/5pjwLt/GTqUPQcO9W76m0w24lWnYCx1iySw
alrl2irL2rnlXJin7q4FttHTWbeH90RVt3050m8ddQKCAQEAkgylgnXlZc+lim1j
1xLdspZt/qM76DP0tDV5RjRK3C3qt5qU47guMl4Hwz+y0v3vJzLl3mk1I6jxfkPp
FewRrTxEVF9OogJqImfFSSCsTuTqBS2fFZF5RGs7svyck/xOOq272sluDlk+BGwf
B5fO+jyQXWkwUQToLosGr3/YvdgWUKe3jd8vZTI4dgTUDk/Ffw/i+nS7LhvQ4fFh
7yEIOyfCH21ngf92g7YrLB1UMdr/a3gCg3hd6PuWFBKisSF8uNg4xE3yfjTbA/cB
FcLSWLHvB67V+aCXkAEp7metoGOBg3D1Akg3nxzf4OQAuXn4BV+sPOzBchZd669w
3IUERQKCAQBxPE7QjBWROKcyTx+TqC/bHE+i6SGLzftlpsQgUZuMzdaz6p5Wue/N
Kf11Kq2pmC73u2VN7nGzFfs1MwWIOLchweRtbeQLk1WutHVJjI8rgHvgpx0cZOOY
OvVR4VVpkKf9QJxdaTElPRpobviqkSG6LAw35VIubNQbzkXxAFOOeGZCEIh3JyQl
9IY6bW8DHOBzw+7GVdifa9DUV8v3RH5bSVXc8yaUK7Ox3TaHrCtQ4RUUdnh1I+Hu
3jUGwvs4LXx96/69GJjrNbSMtTpiO/8NEQJ6p7VBPnrg/pbbpC8fIR8EEySd88qg
sy0PP99EbKbc9POnPk2gofhQ0pinKWEVAoIBAQC0h8J3lGSG9Ki9QJvAOdMqnsSg
uoqjI0y6RmeR7LpYX1ASKNNImjG1hwLyZ8Qg5hCNjySEqBAGGd3MBNfIC6wzBDrV
zSJIrxjvu+2sfz1nUGdYWjQfhx3cT+yGqT6NEQupmstNukxiu2HDu76pi96AX2mk
TXYXlubIpfL50dWZ0wij0LivH6TPavvjbRZnXNVth1qlZOUtuBGyEwcXb4COtqRq
+nP8AEgzKxbMJFVy3PY5E5JyjB5d1ZPQ2OeYUVbfDEEYqzkorjByCcLdv7O1CHNq
VjUyJsd8F1tuCGPxbcbgyCeIqgDM1JxO4zTMSP+C82Ar7VXkr6Q8hAsCgHQ/
-----END RSA PRIVATE KEY-----

13
pem/public.pem Normal file
View File

@@ -0,0 +1,13 @@
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAw2pdG6wMtuLcP0+k1LFvIb0DQo/oHW2uNJaEJK74plXqp4ztz2dR
b+RQHFLeLuqk4i/zc3b4K3fKPXSlwnVPJCwzPrnyT8jYGOZVlWlETiV9xeJhu6s/
Bh6g1PWz75XjjwV50iv/CEiLNBT23f/3J44wrQzygqNQCiQSALdxWLAEl4l5kHSa
9oMyV70/Uql94/ayMARZsHgp9ZvqQKbkZPw6yzVMfCBxQozlNlo315OHevudhnhp
DRjN5I7zWmqYt6rbXJJC7Y3Izdvzn7QI88RqjSRST5I/7Kz3ndCqrOnI+OQUE5NT
REyQebphvQfTDTKlRPXkdyktdK2DH28Zj6ZF3yjQvN35Q4zhOzlq77dO5IhhopI7
ct8dZH1T1nYkvdyCA/EVMtQsASmBOitH0Y0ACoXQK5Kb6nm/TcM/9ZSJUNiEMuy5
gBZ3YKE9oa4cpTpPXwcA+S/cU7HPNnQAsvD3iJi8GTW9uJs84pn4/WhpQqmXd4rv
hKWECCN3fHy01fUs/U0PaSj2jDY/kQVeXoikNMzPUjdZd9m816TIBh3v3aVXCH/0
iTHHAxctvDgMRb2fpvRJ/wwnYjFG9RpamVFDMvC9NffuYzWAA9IRIY4cqgerfHrV
Z2HHiPTDDvDAIsvImXZc/h7mXN6m3RCQ4Qywy993wd9gUdgg/qnynHcCAwEAAQ==
-----END RSA PUBLIC KEY-----

View File

@@ -7,51 +7,258 @@ import (
func init() { func init() {
beego.GlobalControllerRouter["oc-auth/controllers:AuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:AuthController"], beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Post", Method: "GetAll",
Router: `/`, Router: `/`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:AuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:AuthController"],
beego.ControllerComments{
Method: "Get",
Router: `/:authId`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:AuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:AuthController"], beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Post",
Router: `/:id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Get",
Router: `/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Delete", Method: "Delete",
Router: `/:authId`, Router: `/:id`,
AllowHTTPMethods: []string{"delete"}, AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:AuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:AuthController"], beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Find", Method: "Bind",
Router: `/find/:query`, Router: `/:user_id/:group_id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "UnBind",
Router: `/:user_id/:group_id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "Clear",
Router: `/clear`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:GroupController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:GroupController"],
beego.ControllerComments{
Method: "GetByUser",
Router: `/user/:id`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RegistrationController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RegistrationController"], beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{
Method: "InternaisDraftlAuthForward",
Router: `/forward`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{
Method: "Introspect",
Router: `/introspect`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{
Method: "Login",
Router: `/login`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{
Method: "LogOut",
Router: `/logout`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:OAuthController"],
beego.ControllerComments{
Method: "Refresh",
Router: `/refresh`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "GetAll",
Router: `/`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "Get",
Router: `/:id/:relation`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "Bind",
Router: `/:permission_id/:role_id/:relation`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "UnBind",
Router: `/:permission_id/:role_id/:relation`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "Clear",
Router: `/clear`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "GetByRole",
Router: `/role/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:PermissionController"],
beego.ControllerComments{
Method: "GetByUser",
Router: `/user/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "GetAll",
Router: `/`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Post", Method: "Post",
Router: `/`, Router: `/:id`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "Get",
Router: `/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "Delete",
Router: `/:id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "Bind",
Router: `/:user_id/:role_id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "UnBind",
Router: `/:user_id/:role_id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "Clear",
Router: `/clear`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:RoleController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:RoleController"],
beego.ControllerComments{
Method: "GetByUser",
Router: `/user/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:VersionController"], beego.GlobalControllerRouter["oc-auth/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:VersionController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "GetAll", Method: "GetAll",
@@ -61,4 +268,13 @@ func init() {
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-auth/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-auth/controllers:VersionController"],
beego.ControllerComments{
Method: "Get",
Router: `/discovery`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
} }

View File

@@ -14,15 +14,23 @@ import (
) )
func init() { func init() {
ns := beego.NewNamespace("/oc", ns := beego.NewNamespace("/oc/",
beego.NSNamespace("/auth", beego.NSInclude(
&controllers.OAuthController{},
),
beego.NSNamespace("/group",
beego.NSInclude( beego.NSInclude(
&controllers.AuthController{}, &controllers.GroupController{},
), ),
), ),
beego.NSNamespace("/registration", beego.NSNamespace("/role",
beego.NSInclude( beego.NSInclude(
&controllers.RegistrationController{}, &controllers.RoleController{},
),
),
beego.NSNamespace("/permission",
beego.NSInclude(
&controllers.PermissionController{},
), ),
), ),
beego.NSNamespace("/version", beego.NSNamespace("/version",
@@ -31,6 +39,5 @@ func init() {
), ),
), ),
) )
beego.AddNamespace(ns) beego.AddNamespace(ns)
} }

View File

@@ -9,131 +9,139 @@
"email": "admin@o-cloud.io" "email": "admin@o-cloud.io"
}, },
"license": { "license": {
"name": "MIT", "name": "AGPL",
"url": "https://opensource.org/license/mit" "url": "https://www.gnu.org/licenses/agpl-3.0.html"
} }
}, },
"basePath": "/oc", "basePath": "/oc/",
"paths": { "paths": {
"/auth/": { "/forward": {
"get": {
"tags": [
"oc-auth/controllersOAuthController"
],
"description": "auth forward\n\u003cbr\u003e",
"operationId": "OAuthController.AuthForward",
"parameters": [
{
"in": "header",
"name": "Authorization",
"description": "auth token",
"type": "string"
}
],
"responses": {
"200": {
"description": "{string}"
}
}
}
},
"/group/": {
"get": {
"tags": [
"group"
],
"description": "find groups\n\u003cbr\u003e",
"operationId": "GroupController.GetAll",
"responses": {
"200": {
"description": "{group} string"
}
}
}
},
"/group/clear": {
"delete": {
"tags": [
"group"
],
"description": "clear the group\n\u003cbr\u003e",
"operationId": "GroupController.Clear",
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/group/user/{id}": {
"get": {
"tags": [
"group"
],
"description": "find group by user id\n\u003cbr\u003e",
"operationId": "GroupController.GetByUser",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} string"
}
}
}
},
"/group/{id}": {
"get": {
"tags": [
"group"
],
"description": "find group by id\n\u003cbr\u003e",
"operationId": "GroupController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{group} string"
}
}
},
"post": { "post": {
"tags": [ "tags": [
"auth" "group"
], ],
"description": "create auths\n\u003cbr\u003e", "description": "create group\n\u003cbr\u003e",
"operationId": "AuthController.Create", "operationId": "GroupController.Create",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The auth content",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/models.auth"
}
}
}
],
"responses": {
"200": {
"description": "{string} models.auth.Id"
},
"403": {
"description": "body is empty"
}
}
}
},
"/auth/discover/{url}": {
"get": {
"tags": [
"auth"
],
"description": "find auth by authid\n\u003cbr\u003e",
"operationId": "AuthController.Get",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "authId", "name": "id",
"description": "the authid you want to get", "description": "the id you want to get",
"required": true, "required": true,
"type": "string" "type": "string"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "{auth} models.auth" "description": "{auth} create success!"
},
"403": {
"description": ":authId is empty"
}
}
}
},
"/auth/find/{query}": {
"get": {
"tags": [
"auth"
],
"description": "find auths with query\n\u003cbr\u003e",
"operationId": "AuthController.Find",
"parameters": [
{
"in": "path",
"name": "query",
"description": "the keywords you need",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auths} []models.auth"
},
"403": {
"description": ""
}
}
}
},
"/auth/{authId}": {
"get": {
"tags": [
"auth"
],
"description": "find auth by authid\n\u003cbr\u003e",
"operationId": "AuthController.Get",
"parameters": [
{
"in": "path",
"name": "authId",
"description": "the authid you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} models.auth"
},
"403": {
"description": ":authId is empty"
} }
} }
}, },
"delete": { "delete": {
"tags": [ "tags": [
"auth" "group"
], ],
"description": "delete the auth\n\u003cbr\u003e", "description": "delete the group\n\u003cbr\u003e",
"operationId": "AuthController.Delete", "operationId": "GroupController.Delete",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "authId", "name": "id",
"description": "The authId you want to delete", "description": "The id you want to delete",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@@ -141,37 +149,522 @@
"responses": { "responses": {
"200": { "200": {
"description": "{string} delete success!" "description": "{string} delete success!"
},
"403": {
"description": "authId is empty"
} }
} }
} }
}, },
"/registration/": { "/group/{user_id}/{group_id}": {
"post": { "post": {
"tags": [ "tags": [
"registration" "group"
], ],
"description": "create auths\n\u003cbr\u003e", "description": "bind the group to user\n\u003cbr\u003e",
"operationId": "RegistrationController.Create", "operationId": "GroupController.Bind",
"parameters": [ "parameters": [
{ {
"in": "body", "in": "path",
"name": "body", "name": "user_id",
"description": "The app info", "description": "The user_id you want to bind",
"required": true, "required": true,
"schema": { "type": "string"
"$ref": "#/definitions/models.Application" },
} {
"in": "path",
"name": "group_id",
"description": "The group_id you want to bind",
"required": true,
"type": "string"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "{string} models.auth.Id" "description": "{string} bind success!"
}
}
},
"delete": {
"tags": [
"group"
],
"description": "unbind the group to user\n\u003cbr\u003e",
"operationId": "GroupController.UnBind",
"parameters": [
{
"in": "path",
"name": "user_id",
"description": "The group_id you want to unbind",
"required": true,
"type": "string"
}, },
"403": { {
"description": "body is empty" "in": "path",
"name": "group_id",
"description": "The user_id you want to unbind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
}
},
"/introspect": {
"get": {
"tags": [
"oc-auth/controllersOAuthController"
],
"description": "introspect token\n\u003cbr\u003e",
"operationId": "OAuthController.Introspection",
"parameters": [
{
"in": "header",
"name": "Authorization",
"description": "auth token",
"type": "string"
}
],
"responses": {
"200": {
"description": "{string}"
}
}
}
},
"/login": {
"post": {
"tags": [
"oc-auth/controllersOAuthController"
],
"description": "authenticate user\n\u003cbr\u003e",
"operationId": "OAuthController.Login",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The workflow content",
"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}"
}
}
}
},
"/logout": {
"delete": {
"tags": [
"oc-auth/controllersOAuthController"
],
"description": "unauthenticate user\n\u003cbr\u003e",
"operationId": "OAuthController.Logout",
"parameters": [
{
"in": "header",
"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}"
}
}
}
},
"/permission/": {
"get": {
"tags": [
"permission"
],
"description": "find permissions\n\u003cbr\u003e",
"operationId": "PermissionController.GetAll",
"responses": {
"200": {
"description": "{permission} string"
}
}
}
},
"/permission/clear": {
"delete": {
"tags": [
"permission"
],
"description": "clear the permission\n\u003cbr\u003e",
"operationId": "PermissionController.Clear",
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/permission/role/{id}": {
"get": {
"tags": [
"permission"
],
"description": "find permission by role id\n\u003cbr\u003e",
"operationId": "PermissionController.GetByRole",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} string"
}
}
}
},
"/permission/user/{id}": {
"get": {
"tags": [
"permission"
],
"description": "find permission by user id\n\u003cbr\u003e",
"operationId": "PermissionController.GetByUser",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} string"
}
}
}
},
"/permission/{id}/{relation}": {
"get": {
"tags": [
"permission"
],
"description": "find auth by permission\n\u003cbr\u003e",
"operationId": "PermissionController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the permission you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} models.auth"
}
}
}
},
"/permission/{permission_id}/{role_id}/{relation}": {
"post": {
"tags": [
"permission"
],
"description": "bind the permission to role\n\u003cbr\u003e",
"operationId": "PermissionController.Bind",
"parameters": [
{
"in": "path",
"name": "role_id",
"description": "The role_id you want to bind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "method",
"description": "The method you want to relate role \u0026 permission",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "permission_id",
"description": "The permission_id you want to bind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
},
"delete": {
"tags": [
"permission"
],
"description": "unbind the permission to role\n\u003cbr\u003e",
"operationId": "PermissionController.UnBind",
"parameters": [
{
"in": "path",
"name": "role_id",
"description": "The role_id you want to unbind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "relation",
"description": "The method you want to unrelate role \u0026 permission",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "permission_id",
"description": "The permission_id you want to unbind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
}
},
"/refresh": {
"post": {
"tags": [
"oc-auth/controllersOAuthController"
],
"description": "introspect token\n\u003cbr\u003e",
"operationId": "OAuthController.Introspection",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The token info",
"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}"
}
}
}
},
"/role/": {
"get": {
"tags": [
"role"
],
"description": "find roles\n\u003cbr\u003e",
"operationId": "RoleController.GetAll",
"responses": {
"200": {
"description": "{role} string"
}
}
}
},
"/role/clear": {
"delete": {
"tags": [
"role"
],
"description": "clear the role\n\u003cbr\u003e",
"operationId": "RoleController.Clear",
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/role/user/{id}": {
"get": {
"tags": [
"role"
],
"description": "find role by user id\n\u003cbr\u003e",
"operationId": "RoleController.GetByUser",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} string"
}
}
}
},
"/role/{id}": {
"get": {
"tags": [
"role"
],
"description": "find role by id\n\u003cbr\u003e",
"operationId": "RoleController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{role} string"
}
}
},
"post": {
"tags": [
"role"
],
"description": "create role\n\u003cbr\u003e",
"operationId": "RoleController.Create",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{auth} create success!"
}
}
},
"delete": {
"tags": [
"role"
],
"description": "delete the role\n\u003cbr\u003e",
"operationId": "RoleController.Delete",
"parameters": [
{
"in": "path",
"name": "id",
"description": "The id you want to delete",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} delete success!"
}
}
}
},
"/role/{user_id}/{role_id}": {
"post": {
"tags": [
"role"
],
"description": "bind the role to user\n\u003cbr\u003e",
"operationId": "RoleController.Bind",
"parameters": [
{
"in": "path",
"name": "user_id",
"description": "The user_id you want to bind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "role_id",
"description": "The role_id you want to bind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
}
}
},
"delete": {
"tags": [
"role"
],
"description": "unbind the role to user\n\u003cbr\u003e",
"operationId": "RoleController.UnBind",
"parameters": [
{
"in": "path",
"name": "role_id",
"description": "The role_id you want to unbind",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "user_id",
"description": "The user_id you want to unbind",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{string} bind success!"
} }
} }
} }
@@ -189,42 +682,47 @@
} }
} }
} }
},
"/version/discovery": {
"get": {
"tags": [
"version"
],
"description": "get version\n\u003cbr\u003e",
"operationId": "VersionController.Get",
"responses": {
"200": {
"description": ""
}
}
}
} }
}, },
"definitions": { "definitions": {
"models.Application": { "models.Token": {
"title": "Application", "title": "Token",
"type": "object", "type": "object"
"properties": {
"access_token": {
"type": "string"
},
"callback_url": {
"type": "string"
},
"client_id": {
"type": "string"
},
"client_name": {
"type": "string"
},
"client_secret": {
"type": "string"
}
}
}, },
"models.auth": { "models.workflow": {
"title": "auth", "title": "workflow",
"type": "object" "type": "object"
} }
}, },
"tags": [ "tags": [
{ {
"name": "auth", "name": "oc-auth/controllersOAuthController",
"description": "Operations about auth\n" "description": "Operations about auth\n"
}, },
{ {
"name": "registration", "name": "group",
"description": "Operations about auth\n"
},
{
"name": "role",
"description": "Operations about auth\n"
},
{
"name": "permission",
"description": "Operations about auth\n" "description": "Operations about auth\n"
}, },
{ {

View File

@@ -8,127 +8,497 @@ info:
contact: contact:
email: admin@o-cloud.io email: admin@o-cloud.io
license: license:
name: MIT name: AGPL
url: https://opensource.org/license/mit url: https://www.gnu.org/licenses/agpl-3.0.html
basePath: /oc basePath: /oc/
paths: paths:
/auth/: /forward:
post:
tags:
- auth
description: |-
create auths
<br>
operationId: AuthController.Create
parameters:
- in: body
name: body
description: The auth content
required: true
schema:
type: array
items:
$ref: '#/definitions/models.auth'
responses:
"200":
description: '{string} models.auth.Id'
"403":
description: body is empty
/auth/{authId}:
get: get:
tags: tags:
- auth - oc-auth/controllersOAuthController
description: |- description: |-
find auth by authid auth forward
<br> <br>
operationId: AuthController.Get operationId: OAuthController.AuthForward
parameters:
- in: header
name: Authorization
description: auth token
type: string
responses:
"200":
description: '{string}'
/group/:
get:
tags:
- group
description: |-
find groups
<br>
operationId: GroupController.GetAll
responses:
"200":
description: '{group} string'
/group/{id}:
get:
tags:
- group
description: |-
find group by id
<br>
operationId: GroupController.Get
parameters: parameters:
- in: path - in: path
name: authId name: id
description: the authid you want to get description: the id you want to get
required: true required: true
type: string type: string
responses: responses:
"200": "200":
description: '{auth} models.auth' description: '{group} string'
"403": post:
description: :authId is empty
delete:
tags: tags:
- auth - group
description: |- description: |-
delete the auth create group
<br> <br>
operationId: AuthController.Delete operationId: GroupController.Create
parameters: parameters:
- in: path - in: path
name: authId name: id
description: The authId you want to delete description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} create success!'
delete:
tags:
- group
description: |-
delete the group
<br>
operationId: GroupController.Delete
parameters:
- in: path
name: id
description: The id you want to delete
required: true required: true
type: string type: string
responses: responses:
"200": "200":
description: '{string} delete success!' description: '{string} delete success!'
"403": /group/{user_id}/{group_id}:
description: authId is empty post:
/auth/discover/{url}:
get:
tags: tags:
- auth - group
description: |- description: |-
find auth by authid bind the group to user
<br> <br>
operationId: AuthController.Get operationId: GroupController.Bind
parameters: parameters:
- in: path - in: path
name: authId name: user_id
description: the authid you want to get description: The user_id you want to bind
required: true
type: string
- in: path
name: group_id
description: The group_id you want to bind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
delete:
tags:
- group
description: |-
unbind the group to user
<br>
operationId: GroupController.UnBind
parameters:
- in: path
name: user_id
description: The group_id you want to unbind
required: true
type: string
- in: path
name: group_id
description: The user_id you want to unbind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
/group/clear:
delete:
tags:
- group
description: |-
clear the group
<br>
operationId: GroupController.Clear
responses:
"200":
description: '{string} delete success!'
/group/user/{id}:
get:
tags:
- group
description: |-
find group by user id
<br>
operationId: GroupController.GetByUser
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} string'
/introspect:
get:
tags:
- oc-auth/controllersOAuthController
description: |-
introspect token
<br>
operationId: OAuthController.Introspection
parameters:
- in: header
name: Authorization
description: auth token
type: string
responses:
"200":
description: '{string}'
/login:
post:
tags:
- oc-auth/controllersOAuthController
description: |-
authenticate user
<br>
operationId: OAuthController.Login
parameters:
- in: body
name: body
description: The workflow content
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}'
/logout:
delete:
tags:
- oc-auth/controllersOAuthController
description: |-
unauthenticate user
<br>
operationId: OAuthController.Logout
parameters:
- in: header
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}'
/permission/:
get:
tags:
- permission
description: |-
find permissions
<br>
operationId: PermissionController.GetAll
responses:
"200":
description: '{permission} string'
/permission/{id}/{relation}:
get:
tags:
- permission
description: |-
find auth by permission
<br>
operationId: PermissionController.Get
parameters:
- in: path
name: id
description: the permission you want to get
required: true required: true
type: string type: string
responses: responses:
"200": "200":
description: '{auth} models.auth' description: '{auth} models.auth'
"403": /permission/{permission_id}/{role_id}/{relation}:
description: :authId is empty post:
/auth/find/{query}:
get:
tags: tags:
- auth - permission
description: |- description: |-
find auths with query bind the permission to role
<br> <br>
operationId: AuthController.Find operationId: PermissionController.Bind
parameters: parameters:
- in: path - in: path
name: query name: role_id
description: the keywords you need description: The role_id you want to bind
required: true
type: string
- in: path
name: method
description: The method you want to relate role & permission
required: true
type: string
- in: path
name: permission_id
description: The permission_id you want to bind
required: true required: true
type: string type: string
responses: responses:
"200": "200":
description: '{auths} []models.auth' description: '{string} bind success!'
"403": delete:
description: "" tags:
/registration/: - permission
description: |-
unbind the permission to role
<br>
operationId: PermissionController.UnBind
parameters:
- in: path
name: role_id
description: The role_id you want to unbind
required: true
type: string
- in: path
name: relation
description: The method you want to unrelate role & permission
required: true
type: string
- in: path
name: permission_id
description: The permission_id you want to unbind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
/permission/clear:
delete:
tags:
- permission
description: |-
clear the permission
<br>
operationId: PermissionController.Clear
responses:
"200":
description: '{string} delete success!'
/permission/role/{id}:
get:
tags:
- permission
description: |-
find permission by role id
<br>
operationId: PermissionController.GetByRole
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} string'
/permission/user/{id}:
get:
tags:
- permission
description: |-
find permission by user id
<br>
operationId: PermissionController.GetByUser
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} string'
/refresh:
post: post:
tags: tags:
- registration - oc-auth/controllersOAuthController
description: |- description: |-
create auths introspect token
<br> <br>
operationId: RegistrationController.Create operationId: OAuthController.Introspection
parameters: parameters:
- in: body - in: body
name: body name: body
description: The app info description: The token info
required: true required: true
schema: schema:
$ref: '#/definitions/models.Application' $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} models.auth.Id' description: '{string}'
"403": /role/:
description: body is empty get:
tags:
- role
description: |-
find roles
<br>
operationId: RoleController.GetAll
responses:
"200":
description: '{role} string'
/role/{id}:
get:
tags:
- role
description: |-
find role by id
<br>
operationId: RoleController.Get
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{role} string'
post:
tags:
- role
description: |-
create role
<br>
operationId: RoleController.Create
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} create success!'
delete:
tags:
- role
description: |-
delete the role
<br>
operationId: RoleController.Delete
parameters:
- in: path
name: id
description: The id you want to delete
required: true
type: string
responses:
"200":
description: '{string} delete success!'
/role/{user_id}/{role_id}:
post:
tags:
- role
description: |-
bind the role to user
<br>
operationId: RoleController.Bind
parameters:
- in: path
name: user_id
description: The user_id you want to bind
required: true
type: string
- in: path
name: role_id
description: The role_id you want to bind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
delete:
tags:
- role
description: |-
unbind the role to user
<br>
operationId: RoleController.UnBind
parameters:
- in: path
name: role_id
description: The role_id you want to unbind
required: true
type: string
- in: path
name: user_id
description: The user_id you want to unbind
required: true
type: string
responses:
"200":
description: '{string} bind success!'
/role/clear:
delete:
tags:
- role
description: |-
clear the role
<br>
operationId: RoleController.Clear
responses:
"200":
description: '{string} delete success!'
/role/user/{id}:
get:
tags:
- role
description: |-
find role by user id
<br>
operationId: RoleController.GetByUser
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{auth} string'
/version/: /version/:
get: get:
tags: tags:
@@ -140,29 +510,35 @@ paths:
responses: responses:
"200": "200":
description: "" description: ""
/version/discovery:
get:
tags:
- version
description: |-
get version
<br>
operationId: VersionController.Get
responses:
"200":
description: ""
definitions: definitions:
models.Application: models.Token:
title: Application title: Token
type: object type: object
properties: models.workflow:
access_token: title: workflow
type: string
callback_url:
type: string
client_id:
type: string
client_name:
type: string
client_secret:
type: string
models.auth:
title: auth
type: object type: object
tags: tags:
- name: auth - name: oc-auth/controllersOAuthController
description: | description: |
Operations about auth Operations about auth
- name: registration - name: group
description: |
Operations about auth
- name: role
description: |
Operations about auth
- name: permission
description: | description: |
Operations about auth Operations about auth
- name: version - name: version