Skip to content

Commit 93eedac

Browse files
authored
Use RBAC Core (#20)
Also optimizes RBAC calculation.
1 parent 71da5c4 commit 93eedac

File tree

8 files changed

+61
-63
lines changed

8 files changed

+61
-63
lines changed

charts/identity/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: A Helm chart for deploying Unikorn's IdP
44

55
type: application
66

7-
version: v0.1.16
8-
appVersion: v0.1.16
7+
version: v0.1.17
8+
appVersion: v0.1.17
99

1010
icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/go-jose/go-jose/v3 v3.0.1
1111
github.com/google/uuid v1.6.0
1212
github.com/spf13/pflag v1.0.5
13-
github.com/unikorn-cloud/core v0.1.12
13+
github.com/unikorn-cloud/core v0.1.15
1414
go.opentelemetry.io/otel v1.24.0
1515
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0
1616
go.opentelemetry.io/otel/sdk v1.22.0

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
263263
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
264264
github.com/unikorn-cloud/core v0.1.12 h1:I78A9dNMCMtth1WGrEEPhktyNOtQ33W52TIRuw2R4XA=
265265
github.com/unikorn-cloud/core v0.1.12/go.mod h1:G45rJ0e5LOdoFcD9C00wSuhe/AMeBC+tczmQSsS+0/Q=
266+
github.com/unikorn-cloud/core v0.1.13-0.20240322093852-bcfb70d6af1e h1:ekTbAcjXgU/D4FQLdwES9bGtpaavI6PpfqK1hN1ARxs=
267+
github.com/unikorn-cloud/core v0.1.13-0.20240322093852-bcfb70d6af1e/go.mod h1:G45rJ0e5LOdoFcD9C00wSuhe/AMeBC+tczmQSsS+0/Q=
268+
github.com/unikorn-cloud/core v0.1.13 h1:ewIi0wrvFa8u43Yb2IBEB+i5FaRqjcgJh4FWlwwXyOE=
269+
github.com/unikorn-cloud/core v0.1.13/go.mod h1:G45rJ0e5LOdoFcD9C00wSuhe/AMeBC+tczmQSsS+0/Q=
270+
github.com/unikorn-cloud/core v0.1.14 h1:Nk/2g40sf2IAEwQa1fDFcjINInFrw9sOa4rcnCNTQfQ=
271+
github.com/unikorn-cloud/core v0.1.14/go.mod h1:G45rJ0e5LOdoFcD9C00wSuhe/AMeBC+tczmQSsS+0/Q=
272+
github.com/unikorn-cloud/core v0.1.15 h1:avOh0uSvlgSgBzVdM/JUsdP0eYYO8pTaD0oQSUNLGDY=
273+
github.com/unikorn-cloud/core v0.1.15/go.mod h1:G45rJ0e5LOdoFcD9C00wSuhe/AMeBC+tczmQSsS+0/Q=
266274
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
267275
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
268276
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=

pkg/authorization/authenticator.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ limitations under the License.
1818
package authorization
1919

2020
import (
21-
"net/http"
22-
23-
"github.com/unikorn-cloud/core/pkg/authorization/accesstoken"
2421
"github.com/unikorn-cloud/core/pkg/server/errors"
2522
"github.com/unikorn-cloud/identity/pkg/jose"
2623
"github.com/unikorn-cloud/identity/pkg/oauth2"
@@ -44,17 +41,6 @@ func NewAuthenticator(issuer *jose.JWTIssuer, oauth2 *oauth2.Authenticator) *Aut
4441
}
4542
}
4643

47-
func (a *Authenticator) Userinfo(r *http.Request) (interface{}, error) {
48-
token := accesstoken.FromContext(r.Context())
49-
50-
claims, err := a.OAuth2.Verify(r, token)
51-
if err != nil {
52-
return nil, err
53-
}
54-
55-
return claims, nil
56-
}
57-
5844
func (a *Authenticator) JWKS() (interface{}, error) {
5945
result, err := a.issuer.JWKS()
6046
if err != nil {

pkg/handler/handler.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"net/http"
2424

25+
"github.com/unikorn-cloud/core/pkg/authorization/userinfo"
2526
"github.com/unikorn-cloud/core/pkg/server/errors"
2627
"github.com/unikorn-cloud/identity/pkg/authorization"
2728
"github.com/unikorn-cloud/identity/pkg/generated"
@@ -134,14 +135,8 @@ func (h *Handler) PostOauth2V2Token(w http.ResponseWriter, r *http.Request) {
134135
}
135136

136137
func (h *Handler) GetOauth2V2Userinfo(w http.ResponseWriter, r *http.Request) {
137-
result, err := h.authenticator.Userinfo(r)
138-
if err != nil {
139-
errors.HandleError(w, r, err)
140-
return
141-
}
142-
143138
h.setUncacheable(w)
144-
util.WriteJSONResponse(w, r, http.StatusOK, result)
139+
util.WriteJSONResponse(w, r, http.StatusOK, userinfo.FromContext(r.Context()))
145140
}
146141

147142
func (h *Handler) GetOauth2V2Jwks(w http.ResponseWriter, r *http.Request) {

pkg/middleware/openapi/local/authorizer.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import (
2121
"net/http"
2222
"strings"
2323

24-
"github.com/coreos/go-oidc/v3/oidc"
2524
"github.com/getkin/kin-openapi/openapi3filter"
2625

26+
"github.com/unikorn-cloud/core/pkg/authorization/userinfo"
2727
"github.com/unikorn-cloud/core/pkg/server/errors"
2828
"github.com/unikorn-cloud/identity/pkg/oauth2"
2929
)
@@ -57,7 +57,7 @@ func getHTTPAuthenticationScheme(r *http.Request) (string, string, error) {
5757
}
5858

5959
// authorizeOAuth2 checks APIs that require and oauth2 bearer token.
60-
func (a *Authorizer) authorizeOAuth2(r *http.Request) (string, *oidc.UserInfo, error) {
60+
func (a *Authorizer) authorizeOAuth2(r *http.Request) (string, *userinfo.UserInfo, error) {
6161
authorizationScheme, token, err := getHTTPAuthenticationScheme(r)
6262
if err != nil {
6363
return "", nil, err
@@ -68,15 +68,26 @@ func (a *Authorizer) authorizeOAuth2(r *http.Request) (string, *oidc.UserInfo, e
6868
}
6969

7070
// Check the token is from us, for us, and in date.
71-
if _, err := a.authenticator.Verify(r, token); err != nil {
71+
claims, err := a.authenticator.Verify(r, token)
72+
if err != nil {
7273
return "", nil, errors.OAuth2AccessDenied("token validation failed").WithError(err)
7374
}
7475

75-
return token, nil, nil
76+
permissions, err := a.authenticator.GetRBAC().UserPermissions(r.Context(), claims.Subject)
77+
if err != nil {
78+
return "", nil, errors.OAuth2AccessDenied("failed to get user permissions").WithError(err)
79+
}
80+
81+
ui := &userinfo.UserInfo{
82+
Claims: claims.Claims,
83+
RBAC: permissions,
84+
}
85+
86+
return token, ui, nil
7687
}
7788

7889
// Authorize checks the request against the OpenAPI security scheme.
79-
func (a *Authorizer) Authorize(authentication *openapi3filter.AuthenticationInput) (string, *oidc.UserInfo, error) {
90+
func (a *Authorizer) Authorize(authentication *openapi3filter.AuthenticationInput) (string, *userinfo.UserInfo, error) {
8091
if authentication.SecurityScheme.Type == "oauth2" {
8192
return a.authorizeOAuth2(authentication.RequestValidationInput.Request)
8293
}

pkg/oauth2/federated.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ func New(options *Options, namespace string, client client.Client, issuer *jose.
8686
}
8787
}
8888

89+
func (a *Authenticator) GetRBAC() *rbac.RBAC {
90+
return a.rbac
91+
}
92+
8993
type Error string
9094

9195
const (

pkg/rbac/rbac.go

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,13 @@ import (
2121
"slices"
2222
"strings"
2323

24+
"github.com/unikorn-cloud/core/pkg/authorization/rbac"
2425
"github.com/unikorn-cloud/core/pkg/authorization/roles"
2526
unikornv1 "github.com/unikorn-cloud/identity/pkg/apis/unikorn/v1alpha1"
2627

2728
"sigs.k8s.io/controller-runtime/pkg/client"
2829
)
2930

30-
// GroupPermissions are privilege grants for a project.
31-
type GroupPermissions struct {
32-
// ID is the unique, immutable project identifier.
33-
ID string `json:"id"`
34-
// Roles are the privileges a user has for the group.
35-
Roles []roles.Role `json:"roles"`
36-
}
37-
38-
// OrganizationPermissions are privilege grants for an organization.
39-
type OrganizationPermissions struct {
40-
// IsAdmin allows the user to play with all resources in an organization.
41-
IsAdmin bool `json:"isAdmin,omitempty"`
42-
// Name is the name of the organization.
43-
Name string `json:"name"`
44-
// Groups are any groups the user belongs to in an organization.
45-
Groups []GroupPermissions `json:"groups,omitempty"`
46-
}
47-
48-
// Permissions are privilege grants for the entire system.
49-
type Permissions struct {
50-
// IsSuperAdmin HAS SUPER COW POWERS!!!
51-
IsSuperAdmin bool `json:"isSuperAdmin,omitempty"`
52-
// Organizations are any organizations the user has access to.
53-
Organizations []OrganizationPermissions `json:"organizations,omitempty"`
54-
}
55-
5631
// RBAC contains all the scoping rules for services across the platform.
5732
type RBAC struct {
5833
client client.Client
@@ -80,16 +55,20 @@ func (r *RBAC) GetOrganizatons(ctx context.Context) (*unikornv1.OrganizationList
8055

8156
// UserPermissions builds up a hierarchy of permissions for a user, this is used
8257
// both internally and given out to resource servers via token introspection.
83-
func (r *RBAC) UserPermissions(ctx context.Context, email string) (*Permissions, error) {
84-
permissions := &Permissions{}
58+
//
59+
//nolint:cyclop
60+
func (r *RBAC) UserPermissions(ctx context.Context, email string) (*rbac.Permissions, error) {
61+
permissions := &rbac.Permissions{}
8562

8663
organizations, err := r.GetOrganizatons(ctx)
8764
if err != nil {
8865
return nil, err
8966
}
9067

9168
for _, organization := range organizations.Items {
92-
organizationPermissions := OrganizationPermissions{}
69+
var isAdmin bool
70+
71+
var groups []rbac.GroupPermissions
9372

9473
for _, group := range organization.Spec.Groups {
9574
// TODO: implicit groups.
@@ -104,18 +83,33 @@ func (r *RBAC) UserPermissions(ctx context.Context, email string) (*Permissions,
10483

10584
// Hoist admin powers.
10685
if slices.Contains(group.Roles, roles.Admin) {
107-
organizationPermissions.IsAdmin = true
86+
isAdmin = true
10887
}
10988

110-
organizationPermissions.Groups = append(organizationPermissions.Groups, GroupPermissions{
89+
// Remove any special roles.
90+
minifiedRoles := slices.DeleteFunc(group.Roles, func(role roles.Role) bool {
91+
return role == roles.SuperAdmin || role == roles.Admin
92+
})
93+
94+
if len(minifiedRoles) == 0 {
95+
continue
96+
}
97+
98+
groups = append(groups, rbac.GroupPermissions{
11199
ID: group.ID,
112-
Roles: group.Roles,
100+
Roles: minifiedRoles,
113101
})
114102
}
115103

116-
if organizationPermissions.IsAdmin || len(organizationPermissions.Groups) > 0 {
117-
permissions.Organizations = append(permissions.Organizations, organizationPermissions)
104+
if !isAdmin && len(groups) == 0 {
105+
continue
118106
}
107+
108+
permissions.Organizations = append(permissions.Organizations, rbac.OrganizationPermissions{
109+
Name: organization.Name,
110+
IsAdmin: isAdmin,
111+
Groups: groups,
112+
})
119113
}
120114

121115
return permissions, nil

0 commit comments

Comments
 (0)