mirror of
https://github.com/paralus/paralus.git
synced 2026-05-20 15:23:14 +00:00
Improve auth service
This commit is contained in:
132
pkg/auth/v3/core.go
Normal file
132
pkg/auth/v3/core.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package authv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
|
||||
authzv1 "github.com/RafayLabs/rcloud-base/proto/types/authz"
|
||||
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidAPIKey is returned when api key is invalid
|
||||
ErrInvalidAPIKey = errors.New("invalid api key")
|
||||
// ErrInvalidSignature is returns when signature is invalid
|
||||
ErrInvalidSignature = errors.New("invalid signature")
|
||||
)
|
||||
|
||||
func (ac *authContext) IsRequestAllowed(ctx context.Context, req *commonv3.IsRequestAllowedRequest) (*commonv3.IsRequestAllowedResponse, error) {
|
||||
res := &commonv3.IsRequestAllowedResponse{
|
||||
Status: commonv3.RequestStatus_Unknown,
|
||||
SessionData: &commonv3.SessionData{},
|
||||
}
|
||||
|
||||
// Authenticate request
|
||||
succ, err := ac.authenticate(ctx, req, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Don't bother checking authorization if athentication failed
|
||||
if !succ {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if req.NoAuthz {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Authorize request
|
||||
err = ac.authorize(ctx, req, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getTokenCheckSum(body []byte) string {
|
||||
hash := md5.New()
|
||||
hash.Write(body)
|
||||
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
// authenticate validate whether the request is from a legitimate user
|
||||
// and populate relevant information in res.
|
||||
func (ac *authContext) authenticate(ctx context.Context, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) (bool, error) {
|
||||
if len(req.XApiKey) > 0 && len(req.XSessionToken) == 0 {
|
||||
resp, err := ac.ks.GetByKey(ctx, &rpcv3.ApiKeyRequest{
|
||||
Id: req.XApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
_log.Infow("unable to get api key", "key", req.XApiKey, "error", err)
|
||||
return false, ErrInvalidAPIKey
|
||||
}
|
||||
if !(req.XApiToken == getTokenCheckSum([]byte(resp.Secret))) {
|
||||
return false, ErrInvalidSignature
|
||||
}
|
||||
_log.Info("successfully validated api key ", req.XApiKey)
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
res.SessionData.Username = resp.Name
|
||||
res.SessionData.Account = resp.AccountID.String()
|
||||
} else {
|
||||
tsr := ac.kc.V0alpha2Api.ToSession(ctx).
|
||||
XSessionToken(req.GetXSessionToken()).
|
||||
Cookie(req.GetCookie())
|
||||
session, _, err := ac.kc.V0alpha2Api.ToSessionExecute(tsr)
|
||||
if err != nil {
|
||||
// '401 Unauthorized' if the credentials are invalid or no credentials were sent.
|
||||
if strings.Contains(err.Error(), "401 Unauthorized") {
|
||||
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
||||
res.Reason = "no or invalid credentials"
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if session.GetActive() {
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
res.SessionData.Account = session.Identity.GetId()
|
||||
|
||||
t := session.Identity.Traits.(map[string]interface{})
|
||||
res.SessionData.Username = t["email"].(string)
|
||||
} else {
|
||||
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
||||
res.Reason = "no active session"
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// authorize performs authorization of the request
|
||||
func (ac *authContext) authorize(ctx context.Context, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) error {
|
||||
// user,namespace,project,org,url(perm),method
|
||||
// ones that don't have value should be "*"
|
||||
proj := req.Project
|
||||
if proj == "" {
|
||||
proj = "*"
|
||||
}
|
||||
org := req.Org
|
||||
if org == "" {
|
||||
org = "*"
|
||||
}
|
||||
er := authzv1.EnforceRequest{
|
||||
Params: []string{"u:" + res.SessionData.Username, "*", proj, org, req.Url, req.Method},
|
||||
}
|
||||
authenticated, err := ac.as.Enforce(ctx, &er)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !authenticated.Res {
|
||||
res.Status = commonv3.RequestStatus_RequestMethodOrURLNotAllowed
|
||||
res.Reason = "not authorized to perform action"
|
||||
return nil
|
||||
}
|
||||
|
||||
// the following would already be set in auth, but just in case
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
return nil
|
||||
}
|
||||
@@ -25,14 +25,9 @@ type authMiddleware struct {
|
||||
opt Option
|
||||
}
|
||||
|
||||
type remoteAuthMiddleware struct {
|
||||
as rpcv3.AuthClient
|
||||
db *bun.DB
|
||||
opt Option
|
||||
}
|
||||
|
||||
// NewAuthMiddleware creates as a middleware for the HTTP server which
|
||||
// does the auth and authz by talking to kratos server and casbin
|
||||
func NewAuthMiddleware(al *zap.Logger, opt Option) negroni.Handler {
|
||||
// Initialize database
|
||||
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(getDSN())))
|
||||
return &authMiddleware{
|
||||
ac: SetupAuthContext(al),
|
||||
@@ -41,6 +36,15 @@ func NewAuthMiddleware(al *zap.Logger, opt Option) negroni.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
type remoteAuthMiddleware struct {
|
||||
as rpcv3.AuthClient
|
||||
db *bun.DB
|
||||
opt Option
|
||||
}
|
||||
|
||||
// NewRemoteAuthMiddleware creates a middleware for the HTTP server
|
||||
// which does auth and authz by talking to the auth service exposed by
|
||||
// rcloud-base via grpc.
|
||||
func NewRemoteAuthMiddleware(al *zap.Logger, as string, opt Option) negroni.Handler {
|
||||
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(getDSN())))
|
||||
conn, err := grpc.Dial(as, grpc.WithInsecure())
|
||||
@@ -56,173 +60,114 @@ func NewRemoteAuthMiddleware(al *zap.Logger, as string, opt Option) negroni.Hand
|
||||
}
|
||||
}
|
||||
|
||||
func serveHTTP(opt Option,
|
||||
db *bun.DB,
|
||||
isRequestAllowed func(context.Context, *commonpbv3.IsRequestAllowedRequest) (*commonpbv3.IsRequestAllowedResponse, error),
|
||||
rw http.ResponseWriter,
|
||||
r *http.Request,
|
||||
next http.HandlerFunc,
|
||||
) {
|
||||
for _, ex := range opt.ExcludeURLs {
|
||||
match, err := regexp.MatchString(ex, r.URL.Path)
|
||||
if err != nil {
|
||||
_log.Errorf("failed to match URL expression", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if match {
|
||||
next(rw, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Auth is primarily done via grpc endpoints, this is only used
|
||||
// for endoints which do not go through grpc As of now, it is just
|
||||
// prompt.
|
||||
var poResp dao.ProjectOrg
|
||||
|
||||
if strings.HasPrefix(r.URL.String(), "/v2/debug/prompt/project/") {
|
||||
// /v2/debug/prompt/project/:project/cluster/:cluster_name
|
||||
splits := strings.Split(r.URL.String(), "/")
|
||||
if len(splits) > 5 {
|
||||
// we have to fetch the org info for casbin
|
||||
res, err := dao.GetProjectOrganization(r.Context(), db, splits[5])
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate: unable to find project")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
_log.Info("found project with organization ", res.Organization)
|
||||
poResp = res
|
||||
}
|
||||
} else {
|
||||
// The middleware to only used with routes which does not have
|
||||
// a grpc and so fail for any other requests.
|
||||
_log.Errorf("Failed to authenticate: not a prompt request")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
req := &commonpbv3.IsRequestAllowedRequest{
|
||||
Url: r.URL.String(),
|
||||
Method: r.Method,
|
||||
XSessionToken: r.Header.Get("X-Session-Token"),
|
||||
XApiKey: r.Header.Get("X-API-KEYID"),
|
||||
XApiToken: r.Header.Get("X-API-TOKEN"),
|
||||
Cookie: r.Header.Get("Cookie"),
|
||||
Project: poResp.Project,
|
||||
Org: poResp.Organization,
|
||||
}
|
||||
res, err := isRequestAllowed(r.Context(), req)
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate a request: %s", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s := res.GetStatus()
|
||||
switch s {
|
||||
case commonpbv3.RequestStatus_RequestAllowed:
|
||||
// update the session data response to be used within prompt
|
||||
res.SessionData.Organization = poResp.OrganizationId
|
||||
res.SessionData.Partner = poResp.PartnerId
|
||||
res.SessionData.Project = &commonpbv3.ProjectData{
|
||||
List: []*commonpbv3.ProjectRole{
|
||||
{
|
||||
ProjectId: poResp.ProjectId,
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), common.SessionDataKey, res.SessionData)
|
||||
next(rw, r.WithContext(ctx))
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestMethodOrURLNotAllowed:
|
||||
http.Error(rw, res.GetReason(), http.StatusForbidden)
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestNotAuthenticated:
|
||||
http.Error(rw, res.GetReason(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// status is unknown
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// ServeHTTP function is called by the HTTP server to invoke the
|
||||
// middleware
|
||||
func (am *authMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
for _, ex := range am.opt.ExcludeURLs {
|
||||
match, err := regexp.MatchString(ex, r.URL.Path)
|
||||
if err != nil {
|
||||
_log.Errorf("failed to match URL expression", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if match {
|
||||
next(rw, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Auth is primarily done via grpc endpoints, this is only used
|
||||
// for endoints which do not go through grpc As of now, it is just
|
||||
// prompt.
|
||||
var poResp dao.ProjectOrg
|
||||
|
||||
if strings.HasPrefix(r.URL.String(), "/v2/debug/prompt/project/") {
|
||||
// /v2/debug/prompt/project/:project/cluster/:cluster_name
|
||||
splits := strings.Split(r.URL.String(), "/")
|
||||
if len(splits) > 5 {
|
||||
// we have to fetch the org info for casbin
|
||||
res, err := dao.GetProjectOrganization(r.Context(), am.db, splits[5])
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate: unable to find project")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
_log.Info("found project with organization ", res.Organization)
|
||||
poResp = res
|
||||
}
|
||||
} else {
|
||||
// The middleware to only used with routes which does not have
|
||||
// a grpc and so fail for any other requests.
|
||||
_log.Errorf("Failed to authenticate: not a prompt request")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
req := &commonpbv3.IsRequestAllowedRequest{
|
||||
Url: r.URL.String(),
|
||||
Method: r.Method,
|
||||
XSessionToken: r.Header.Get("X-Session-Token"),
|
||||
XApiKey: r.Header.Get("X-API-KEYID"),
|
||||
XApiToken: r.Header.Get("X-API-TOKEN"),
|
||||
Cookie: r.Header.Get("Cookie"),
|
||||
Project: poResp.Project,
|
||||
Org: poResp.Organization,
|
||||
}
|
||||
res, err := am.ac.IsRequestAllowed(r.Context(), req)
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate a request: %s", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s := res.GetStatus()
|
||||
switch s {
|
||||
case commonpbv3.RequestStatus_RequestAllowed:
|
||||
// update the session data response to be used within prompt
|
||||
res.SessionData.Organization = poResp.OrganizationId
|
||||
res.SessionData.Partner = poResp.PartnerId
|
||||
res.SessionData.Project = &commonpbv3.ProjectData{
|
||||
List: []*commonpbv3.ProjectRole{
|
||||
{
|
||||
ProjectId: poResp.ProjectId,
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), common.SessionDataKey, res.SessionData)
|
||||
next(rw, r.WithContext(ctx))
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestMethodOrURLNotAllowed:
|
||||
http.Error(rw, res.GetReason(), http.StatusForbidden)
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestNotAuthenticated:
|
||||
http.Error(rw, res.GetReason(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// status is unknown
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
serveHTTP(am.opt, am.db, am.ac.IsRequestAllowed, rw, r, next)
|
||||
}
|
||||
|
||||
// Same as previous ServeHTTP, just using remoteAuthMiddleware instead of authMiddleware
|
||||
// ServeHTTP function is called by the HTTP server to invoke the
|
||||
// middleware. Same as previous ServeHTTP, but uses
|
||||
// remoteAuthMiddleware instead of authMiddleware
|
||||
func (am *remoteAuthMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
for _, ex := range am.opt.ExcludeURLs {
|
||||
match, err := regexp.MatchString(ex, r.URL.Path)
|
||||
if err != nil {
|
||||
_log.Errorf("failed to match URL expression", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if match {
|
||||
next(rw, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Auth is primarily done via grpc endpoints, this is only used
|
||||
// for endoints which do not go through grpc As of now, it is just
|
||||
// prompt.
|
||||
var poResp dao.ProjectOrg
|
||||
|
||||
if strings.HasPrefix(r.URL.String(), "/v2/debug/prompt/project/") {
|
||||
// /v2/debug/prompt/project/:project/cluster/:cluster_name
|
||||
splits := strings.Split(r.URL.String(), "/")
|
||||
if len(splits) > 5 {
|
||||
// we have to fetch the org info for casbin
|
||||
res, err := dao.GetProjectOrganization(r.Context(), am.db, splits[5])
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate: unable to find project")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
_log.Info("found project with organization ", res.Organization)
|
||||
poResp = res
|
||||
}
|
||||
} else {
|
||||
// The middleware to only used with routes which does not have
|
||||
// a grpc and so fail for any other requests.
|
||||
_log.Errorf("Failed to authenticate: not a prompt request")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
req := &commonpbv3.IsRequestAllowedRequest{
|
||||
Url: r.URL.String(),
|
||||
Method: r.Method,
|
||||
XSessionToken: r.Header.Get("X-Session-Token"),
|
||||
XApiKey: r.Header.Get("X-API-KEYID"),
|
||||
XApiToken: r.Header.Get("X-API-TOKEN"),
|
||||
Cookie: r.Header.Get("Cookie"),
|
||||
Project: poResp.Project,
|
||||
Org: poResp.Organization,
|
||||
}
|
||||
res, err := am.as.IsRequestAllowed(r.Context(), req)
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate a request: %s", err)
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s := res.GetStatus()
|
||||
switch s {
|
||||
case commonpbv3.RequestStatus_RequestAllowed:
|
||||
// update the session data response to be used within prompt
|
||||
res.SessionData.Organization = poResp.OrganizationId
|
||||
res.SessionData.Partner = poResp.PartnerId
|
||||
res.SessionData.Project = &commonpbv3.ProjectData{
|
||||
List: []*commonpbv3.ProjectRole{
|
||||
{
|
||||
ProjectId: poResp.ProjectId,
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), common.SessionDataKey, res.SessionData)
|
||||
next(rw, r.WithContext(ctx))
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestMethodOrURLNotAllowed:
|
||||
http.Error(rw, res.GetReason(), http.StatusForbidden)
|
||||
return
|
||||
case commonpbv3.RequestStatus_RequestNotAuthenticated:
|
||||
http.Error(rw, res.GetReason(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// status is unknown
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
serveHTTP(
|
||||
am.opt,
|
||||
am.db,
|
||||
func(ctx context.Context, isr *commonpbv3.IsRequestAllowedRequest) (*commonpbv3.IsRequestAllowedResponse, error) {
|
||||
return am.as.IsRequestAllowed(ctx, isr)
|
||||
},
|
||||
rw,
|
||||
r,
|
||||
next)
|
||||
}
|
||||
|
||||
@@ -2,23 +2,10 @@ package authv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
|
||||
authzv1 "github.com/RafayLabs/rcloud-base/proto/types/authz"
|
||||
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidAPIKey is returned when api key is invalid
|
||||
ErrInvalidAPIKey = errors.New("invalid api key")
|
||||
// ErrInvalidSignature is returns when signature is invalid
|
||||
ErrInvalidSignature = errors.New("invalid signature")
|
||||
)
|
||||
|
||||
type authService struct {
|
||||
ac authContext
|
||||
}
|
||||
@@ -31,118 +18,9 @@ func NewAuthService(ac authContext) AuthService {
|
||||
return &authService{ac}
|
||||
}
|
||||
|
||||
// Auth is exposed as an external service so that other modules like
|
||||
// prompt can call into this inorder to authenticate. This will be
|
||||
// made use of using `remoteAuthMiddleware` in other services.
|
||||
func (s *authService) IsRequestAllowed(ctx context.Context, req *commonv3.IsRequestAllowedRequest) (*commonv3.IsRequestAllowedResponse, error) {
|
||||
return s.ac.IsRequestAllowed(ctx, req)
|
||||
}
|
||||
|
||||
func (ac *authContext) IsRequestAllowed(ctx context.Context, req *commonv3.IsRequestAllowedRequest) (*commonv3.IsRequestAllowedResponse, error) {
|
||||
res := &commonv3.IsRequestAllowedResponse{
|
||||
Status: commonv3.RequestStatus_Unknown,
|
||||
SessionData: &commonv3.SessionData{},
|
||||
}
|
||||
|
||||
// Authenticate request
|
||||
succ, err := ac.authenticate(ctx, req, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Don't bother checking authorization if athentication failed
|
||||
if !succ {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if req.NoAuthz {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Authorize request
|
||||
err = ac.authorize(ctx, req, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getTokenCheckSum(body []byte) string {
|
||||
hash := md5.New()
|
||||
hash.Write(body)
|
||||
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
// authenticate validate whether the request is from a legitimate user
|
||||
// and populate relevant information in res.
|
||||
func (ac *authContext) authenticate(ctx context.Context, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) (bool, error) {
|
||||
if len(req.XApiKey) > 0 && len(req.XSessionToken) == 0 {
|
||||
resp, err := ac.ks.GetByKey(ctx, &rpcv3.ApiKeyRequest{
|
||||
Id: req.XApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
_log.Infow("unable to get api key", "key", req.XApiKey, "error", err)
|
||||
return false, ErrInvalidAPIKey
|
||||
}
|
||||
if !(req.XApiToken == getTokenCheckSum([]byte(resp.Secret))) {
|
||||
return false, ErrInvalidSignature
|
||||
}
|
||||
_log.Info("successfully validated api key ", req.XApiKey)
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
res.SessionData.Username = resp.Name
|
||||
res.SessionData.Account = resp.AccountID.String()
|
||||
} else {
|
||||
tsr := ac.kc.V0alpha2Api.ToSession(ctx).
|
||||
XSessionToken(req.GetXSessionToken()).
|
||||
Cookie(req.GetCookie())
|
||||
session, _, err := ac.kc.V0alpha2Api.ToSessionExecute(tsr)
|
||||
if err != nil {
|
||||
// '401 Unauthorized' if the credentials are invalid or no credentials were sent.
|
||||
if strings.Contains(err.Error(), "401 Unauthorized") {
|
||||
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
||||
res.Reason = "no or invalid credentials"
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if session.GetActive() {
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
res.SessionData.Account = session.Identity.GetId()
|
||||
|
||||
t := session.Identity.Traits.(map[string]interface{})
|
||||
res.SessionData.Username = t["email"].(string)
|
||||
} else {
|
||||
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
||||
res.Reason = "no active session"
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// authorize performs authorization of the request
|
||||
func (ac *authContext) authorize(ctx context.Context, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) error {
|
||||
// user,namespace,project,org,url(perm),method
|
||||
// ones that don't have value should be "*"
|
||||
proj := req.Project
|
||||
if proj == "" {
|
||||
proj = "*"
|
||||
}
|
||||
org := req.Org
|
||||
if org == "" {
|
||||
org = "*"
|
||||
}
|
||||
er := authzv1.EnforceRequest{
|
||||
Params: []string{"u:" + res.SessionData.Username, "*", proj, org, req.Url, req.Method},
|
||||
}
|
||||
authenticated, err := ac.as.Enforce(ctx, &er)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !authenticated.Res {
|
||||
res.Status = commonv3.RequestStatus_RequestMethodOrURLNotAllowed
|
||||
res.Reason = "not authorized to perform action"
|
||||
return nil
|
||||
}
|
||||
|
||||
// the following would already be set in auth, but just in case
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user