mirror of
https://github.com/paralus/paralus.git
synced 2026-02-14 17:49:51 +00:00
152 lines
4.4 KiB
Go
152 lines
4.4 KiB
Go
package authv3
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/paralus/paralus/internal/dao"
|
|
rpcv3 "github.com/paralus/paralus/proto/rpc/user"
|
|
authzv1 "github.com/paralus/paralus/proto/types/authz"
|
|
commonv3 "github.com/paralus/paralus/proto/types/commonpb/v3"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
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)
|
|
uid, err := uuid.Parse(session.Identity.Id)
|
|
if err != nil {
|
|
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
|
res.Reason = "unable to find identity"
|
|
return false, err
|
|
}
|
|
groups, err := dao.GetGroups(ctx, ac.db, uid)
|
|
if err != nil {
|
|
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
|
res.Reason = "unable to find identity"
|
|
return false, err
|
|
}
|
|
groupNames := []string{}
|
|
for _, g := range groups {
|
|
groupNames = append(groupNames, g.Name)
|
|
}
|
|
res.SessionData.Groups = groupNames
|
|
} 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
|
|
}
|