mirror of
https://github.com/onepanelio/onepanel.git
synced 2024-08-21 00:00:34 +08:00
158 lines
4.2 KiB
Go
158 lines
4.2 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
api "github.com/onepanelio/core/api/gen"
|
|
v1 "github.com/onepanelio/core/pkg"
|
|
"github.com/onepanelio/core/pkg/util"
|
|
"github.com/onepanelio/core/server/auth"
|
|
"github.com/pkg/errors"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
// AuthServer contains logic for checking Authorization of resources in the system
|
|
type AuthServer struct {
|
|
api.UnimplementedAuthServiceServer
|
|
}
|
|
|
|
// NewAuthServer creates a new AuthServer
|
|
func NewAuthServer() *AuthServer {
|
|
return &AuthServer{}
|
|
}
|
|
|
|
// IsAuthorized checks if the provided action is authorized.
|
|
// No token == unauthorized. This is indicated by a nil ctx.
|
|
// Invalid token == unauthorized.
|
|
// Otherwise, we check with k8s using all of the provided data in the request.
|
|
func (a *AuthServer) IsAuthorized(ctx context.Context, request *api.IsAuthorizedRequest) (res *api.IsAuthorizedResponse, err error) {
|
|
res = &api.IsAuthorizedResponse{}
|
|
if ctx == nil {
|
|
res.Authorized = false
|
|
return res, status.Error(codes.Unauthenticated, "Unauthenticated.")
|
|
}
|
|
//User auth check
|
|
client := getClient(ctx)
|
|
|
|
err = a.isValidToken(err, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//Check the request
|
|
allowed, err := auth.IsAuthorized(client, request.IsAuthorized.Namespace, request.IsAuthorized.Verb, request.IsAuthorized.Group, request.IsAuthorized.Resource, request.IsAuthorized.ResourceName)
|
|
if err != nil {
|
|
res.Authorized = false
|
|
return res, util.NewUserError(codes.PermissionDenied, fmt.Sprintf("Namespace: %v, Verb: %v, Group: \"%v\", Resource: %v. Source: %v", request.IsAuthorized.Namespace, request.IsAuthorized.Verb, request.IsAuthorized.Group, request.IsAuthorized.ResourceName, err))
|
|
}
|
|
|
|
res.Authorized = allowed
|
|
return res, nil
|
|
}
|
|
|
|
// GetAccessToken is an alias for IsValidToken. It returns a token given a username and hashed token.
|
|
func (a *AuthServer) GetAccessToken(ctx context.Context, req *api.GetAccessTokenRequest) (res *api.GetAccessTokenResponse, err error) {
|
|
if ctx == nil {
|
|
return nil, status.Error(codes.Unauthenticated, "unauthenticated")
|
|
}
|
|
|
|
client := getClient(ctx)
|
|
err = a.isValidToken(err, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
config, err := client.GetSystemConfig()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
domain := config.Domain()
|
|
if domain == nil {
|
|
return nil, fmt.Errorf("domain is not set")
|
|
}
|
|
|
|
// This is for backwards compatibility
|
|
// Originally, when you logged in as the admin, you would get the defaultNamespace as the
|
|
// namespace.
|
|
if req.Username == "admin" {
|
|
nsList, err := client.CoreV1().Namespaces().List(metav1.ListOptions{
|
|
LabelSelector: "onepanel.io/defaultNamespace=true",
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(nsList.Items) == 1 {
|
|
req.Username = nsList.Items[0].Name
|
|
}
|
|
}
|
|
|
|
res = &api.GetAccessTokenResponse{
|
|
Domain: *domain,
|
|
AccessToken: client.Token,
|
|
Username: req.Username,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// IsValidToken returns the appropriate token information given an md5 version of the token
|
|
// Deprecated: Use GetAccessToken instead
|
|
func (a *AuthServer) IsValidToken(ctx context.Context, req *api.IsValidTokenRequest) (res *api.IsValidTokenResponse, err error) {
|
|
if ctx == nil {
|
|
return nil, status.Error(codes.Unauthenticated, "unauthenticated")
|
|
}
|
|
|
|
client := getClient(ctx)
|
|
err = a.isValidToken(err, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
config, err := client.GetSystemConfig()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
domain := config.Domain()
|
|
if domain == nil {
|
|
return nil, fmt.Errorf("domain is not set")
|
|
}
|
|
|
|
res = &api.IsValidTokenResponse{
|
|
Domain: *domain,
|
|
Token: client.Token,
|
|
Username: req.Username,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (a *AuthServer) isValidToken(err error, client *v1.Client) error {
|
|
namespaces, err := client.ListOnepanelEnabledNamespaces()
|
|
if err != nil {
|
|
if err.Error() == "Unauthorized" {
|
|
return status.Error(codes.Unauthenticated, "Unauthenticated.")
|
|
}
|
|
return err
|
|
}
|
|
if len(namespaces) == 0 {
|
|
return errors.New("no namespaces for onepanel setup")
|
|
}
|
|
namespace := namespaces[0]
|
|
|
|
allowed, err := auth.IsAuthorized(client, "", "get", "", "namespaces", namespace.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !allowed {
|
|
return status.Error(codes.Unauthenticated, "Unauthenticated.")
|
|
}
|
|
return nil
|
|
}
|