-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add authorization for the API layer
Signed-off-by: Donnie Adams <[email protected]>
- Loading branch information
Showing
18 changed files
with
477 additions
and
362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package authn | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/otto8-ai/otto8/pkg/api/authz" | ||
"k8s.io/apiserver/pkg/authentication/authenticator" | ||
"k8s.io/apiserver/pkg/authentication/user" | ||
) | ||
|
||
type Anonymous struct { | ||
} | ||
|
||
func (n Anonymous) AuthenticateRequest(*http.Request) (*authenticator.Response, bool, error) { | ||
return &authenticator.Response{ | ||
User: &user.DefaultInfo{ | ||
UID: "anonymous", | ||
Name: "anonymous", | ||
Groups: []string{authz.UnauthenticatedGroup}, | ||
}, | ||
}, true, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package authn | ||
|
||
import ( | ||
"net/http" | ||
|
||
"k8s.io/apiserver/pkg/authentication/authenticator" | ||
"k8s.io/apiserver/pkg/authentication/user" | ||
) | ||
|
||
type Authenticator struct { | ||
authenticator authenticator.Request | ||
} | ||
|
||
func NewAuthenticator(authenticator authenticator.Request) *Authenticator { | ||
return &Authenticator{ | ||
authenticator: authenticator, | ||
} | ||
} | ||
|
||
func (a *Authenticator) Authenticate(req *http.Request) (user.Info, error) { | ||
resp, ok, err := a.authenticator.AuthenticateRequest(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !ok { | ||
panic("authentication should always succeed") | ||
} | ||
return resp.User, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package authn | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/otto8-ai/otto8/pkg/api/authz" | ||
"k8s.io/apiserver/pkg/authentication/authenticator" | ||
"k8s.io/apiserver/pkg/authentication/user" | ||
) | ||
|
||
type NoAuth struct { | ||
} | ||
|
||
func (n NoAuth) AuthenticateRequest(*http.Request) (*authenticator.Response, bool, error) { | ||
return &authenticator.Response{ | ||
User: &user.DefaultInfo{ | ||
Name: "nobody", | ||
Groups: []string{authz.AdminGroup, authz.AuthenticatedGroup}, | ||
}, | ||
}, true, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package authz | ||
|
||
import ( | ||
"net/http" | ||
"slices" | ||
|
||
"k8s.io/apiserver/pkg/authentication/user" | ||
) | ||
|
||
const ( | ||
AdminGroup = "admin" | ||
AuthenticatedGroup = "authenticated" | ||
UnauthenticatedGroup = "unauthenticated" | ||
|
||
// anyGroup is an internal group that allows access to any group | ||
anyGroup = "*" | ||
) | ||
|
||
type Authorizer struct { | ||
rules []rule | ||
} | ||
|
||
func NewAuthorizer() *Authorizer { | ||
return &Authorizer{ | ||
rules: defaultRules(), | ||
} | ||
} | ||
|
||
func (a *Authorizer) Authorize(req *http.Request, user user.Info) bool { | ||
userGroups := user.GetGroups() | ||
for _, r := range a.rules { | ||
if r.group == anyGroup || slices.Contains(userGroups, r.group) { | ||
if _, pattern := r.mux.Handler(req); pattern != "" { | ||
return true | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
type rule struct { | ||
group string | ||
mux *http.ServeMux | ||
} | ||
|
||
func defaultRules() []rule { | ||
var rules []rule | ||
|
||
// Build admin mux, admins can assess any URL | ||
adminMux := http.NewServeMux() | ||
adminMux.Handle("/", (*fake)(nil)) | ||
|
||
rules = append(rules, rule{ | ||
group: AdminGroup, | ||
mux: adminMux, | ||
}) | ||
|
||
// Build mux that anyone can access | ||
anyMux := http.NewServeMux() | ||
anyMux.Handle("POST /api/webhooks/{id}", (*fake)(nil)) | ||
|
||
anyMux.Handle("GET /api/token-request/{id}", (*fake)(nil)) | ||
anyMux.Handle("POST /api/token-request", (*fake)(nil)) | ||
anyMux.Handle("GET /api/token-request/{id}/{service}", (*fake)(nil)) | ||
|
||
anyMux.Handle("GET /api/auth-providers", (*fake)(nil)) | ||
anyMux.Handle("GET /api/auth-providers/{slug}", (*fake)(nil)) | ||
|
||
anyMux.Handle("GET /api/oauth/start/{id}/{service}", (*fake)(nil)) | ||
anyMux.Handle("/api/oauth/redirect/{service}", (*fake)(nil)) | ||
|
||
anyMux.Handle("GET /api/app-oauth/authorize/{id}", (*fake)(nil)) | ||
anyMux.Handle("GET /api/app-oauth/refresh/{id}", (*fake)(nil)) | ||
anyMux.Handle("GET /api/app-oauth/callback/{id}", (*fake)(nil)) | ||
anyMux.Handle("GET /api/app-oauth/get-token", (*fake)(nil)) | ||
|
||
rules = append(rules, rule{ | ||
group: anyGroup, | ||
mux: anyMux, | ||
}) | ||
|
||
return rules | ||
} | ||
|
||
// fake is a fake handler that does nothing | ||
type fake struct{} | ||
|
||
func (f *fake) ServeHTTP(http.ResponseWriter, *http.Request) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package api | ||
|
||
import "net/http" | ||
|
||
type Router interface { | ||
HandleFunc(string, HandlerFunc, ...string) | ||
HTTPHandle(string, http.Handler) | ||
} | ||
|
||
type router struct { | ||
authorizedGroups []string | ||
next Router | ||
} | ||
|
||
func (r *router) HandleFunc(pattern string, f HandlerFunc, authorizedGroups ...string) { | ||
r.next.HandleFunc(pattern, f, append(r.authorizedGroups, authorizedGroups...)...) | ||
} | ||
|
||
func (r *router) HTTPHandle(pattern string, f http.Handler) { | ||
r.next.HTTPHandle(pattern, f) | ||
} |
Oops, something went wrong.