Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
479 changes: 479 additions & 0 deletions common/auth/auth.go

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions common/auth/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package auth

import "time"

const (
grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
httpsProtocol = "https"
credKeyPrivateKey = "privateKey"
privateKeyPEMType = "PRIVATE KEY"
sdkIssuer = "sdk"
signedTokenPrefix = "signed_token_"
ctxKeyRegex = `^[a-zA-Z0-9_]+$`
bearerTokenExpiry = 60 * time.Minute
defaultDataTokenTTL = 60 * time.Second

jwtClaimExp = "exp"
jwtClaimCtx = "ctx"
jwtClaimIss = "iss"
jwtClaimAud = "aud"
jwtClaimKey = "key"
jwtClaimIat = "iat"
jwtClaimSub = "sub"
jwtClaimTok = "tok"
)
55 changes: 55 additions & 0 deletions common/auth/messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package auth

// Message bodies without the SDK prefix. The prefix ("Skyflow Go SDK v2.1.0",
// "[ Skyflow Go SDK v2.1.0 ] ", ...) is supplied by the SDK that owns the
// Service, so v2 and flowvault keep their own version string in every error
// and log line while sharing one implementation.

// Error messages: prefixed with Config.SDKPrefix.
const (
errEmptyCredentialFilePath = " Initialization failed. Invalid credentials. Credentials file path must not be empty."
errEmptyCredentialsString = " Initialization failed. Invalid credentials. Credentials string must not be empty."
errFileNotFound = " Initialization failed. Credential file not found at %s. Verify the file path."
errCredentialsStringInvalidJSON = " Initialization failed. Credentials string is not in valid JSON format. Verify the credentials string contents."
errInvalidCredentials = " Initialization failed. Invalid credentials provided. Specify valid credentials."
errMissingPrivateKey = " Initialization failed. Unable to read private key in credentials. Verify your private key."
errMissingClientId = " Initialization failed. Unable to read client ID in credentials. Verify your client ID."
errMissingKeyId = " Initialization failed. Unable to read key ID in credentials. Verify your key ID."
errMissingTokenUri = " Initialization failed. Unable to read token URI in credentials. Verify your token URI."
errInvalidTokenUri = " Initialization failed. Token URI in not a valid URL in credentials. Verify your token URI."
errJwtInvalidFormat = " Initialization failed. Invalid private key format. Verify your credentials."
errInvalidAlgorithm = " Initialization failed. Invalid algorithm to parse private key. Specify valid algorithm."
errInvalidKeySpec = " Initialization failed. Unable to parse RSA private key. Verify your credentials."
errEmptyTokensDetokenize = " Validation error. Invalid data tokens. Specify at least one data token."
errErrorOccurred = " API error. Error occurred."
errUnknownError = " Error occurred. %s"
errInvalidCtxType = " Initialization failed. Invalid ctx type. Specify ctx as a string, number, boolean, or a map[string]interface{}."
errInvalidCtxMapKey = " Initialization failed. Invalid key '%s' in ctx map. Keys must contain only alphanumeric characters and underscores."
)

// Log messages: prefixed with Config.LogPrefix.
const (
logEmptyBearerToken = "BearerToken is Empty"
logBearerTokenExpired = "Bearer Token provided is either invalid or has expired."
logGenerateBearerTokenTriggered = "GenerateBearerToken is triggered"
logGenerateBearerTokenSuccess = "BearerToken is generated"
logGenerateSignedDataTokenSuccess = "Signed Data tokens are generated"
logGenerateSignedDataTokensTriggered = "generateSignedDataTokens is triggered"
logDeprecatedFieldRoleIds = "Deprecated: BearerTokenOptions.RoleIDs is deprecated and will be removed in a future version. Use RoleIds instead."
logClientIdNotFound = "Invalid credentials. Client ID cannot be empty."
logTokenUriNotFound = "Invalid credentials. Token URI cannot be empty."
logKeyIdNotFound = "Invalid credentials. Key ID cannot be empty."
logPrivateKeyNotFound = "Invalid credentials. Private key cannot be empty."
logNotAValidJSON = "Credentials is not in valid JSON format. Verify the credentials."
logFileNotFound = "Credential file not found at %s. Verify the file path."
logInvalidInputFile = "Unable to read credentials - file %s."
logEmptyCredentialsPath = "Invalid credentials. Credentials path can not be empty."
logInvalidCredentialsStringFormat = "Credentials string is not in a valid JSON string format."
logInvalidTokenUri = "Invalid value for token URI in credentials."
logJwtInvalidFormat = "Private key is not in a valid format."
logInvalidAlgorithm = "Algorithm for parsing private key is invalid or does not exist."
logInvalidKeySpec = "Unable to parse RSA private key."
logBearerTokenRejected = "Bearer token request resulted in failure."
logPrivateKeyType = "RSA private key is of the wrong type Pem Type: %s"
logParseJwtPayload = "Unable to parse jwt payload"
)
55 changes: 55 additions & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Package config holds the platform-level configuration types shared by every
// Skyflow Go SDK: environment, credentials, vault and connection configs, and
// custom header keys. Each SDK re-exports them under its own utils/common
// package through type aliases, so they are the same types across SDKs.
//
// Request, option and response types are deliberately NOT here: each SDK owns
// its own, because the vault APIs differ between products.
package config

type Env int

const (
PROD Env = iota
STAGE
SANDBOX
DEV
)

type Credentials struct {
Path string
Roles []string
Context interface{}
CredentialsString string
Token string
ApiKey string
}

type VaultConfig struct {
VaultId string
ClusterId string
BaseVaultUrl string
// Deprecated: Use BaseVaultUrl instead.
BaseVaultURL string
Env Env
Credentials Credentials
}

type ConnectionConfig struct {
ConnectionId string
ConnectionUrl string
Credentials Credentials
}

type CustomHeaderKey string

const (
SkyflowAccountId CustomHeaderKey = "x-skyflow-account-id"
SkyflowAccountName CustomHeaderKey = "x-skyflow-account-name"
RequestIdHeader CustomHeaderKey = "x-request-id"

// Deprecated: Use SkyflowAccountId instead.
SkyflowAccountID = SkyflowAccountId
// Deprecated: Use RequestIdHeader instead.
RequestIDHeader = RequestIdHeader
)
27 changes: 27 additions & 0 deletions common/config/messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package config

// Message bodies without the SDK prefix; the Validator prepends the owning
// SDK's prefix so each SDK reports its own name/version (same pattern as auth).

const (
errInvalidVaultId = " Initialization failed. Invalid vault ID. Specify a valid vault ID."
errInvalidClusterId = " Initialization failed. Invalid cluster ID. Specify cluster ID."
errInvalidVaultUrl = " Initialization failed. Invalid vault URL. Specify a valid vault URL."
errMultipleTokenGenerationMeansPassed = " Initialization failed. Invalid credentials. Specify only one from 'path', 'credentialsString', 'token' or 'apiKey'."
errNoTokenGenerationMeansPassed = " Initialization failed. Invalid credentials. Specify any one from 'path', 'credentialsString', 'token' or 'apiKey'."
errInvalidApiKey = " Initialization failed. Invalid credentials. Specify valid api key."
errEmptyRoles = " Initialization failed. Invalid roles. Specify at least one role."
errEmptyRoleInRoles = " Initialization failed. Invalid role. Specify a valid role."
)

const (
logVaultIdIsRequired = "Invalid vault config. Vault ID is required."
logVaultUrlIsInvalid = "Invalid vault config. Vault URL is not a valid URL."
logClusterIdIsRequired = "Invalid vault config. Cluster ID is required."
logDeprecatedFieldBaseVaultUrl = "Deprecated: VaultConfig.BaseVaultURL is deprecated and will be removed in a future version. Use BaseVaultUrl instead."
logMultipleTokenGenerationMeansPassed = "Invalid credentials. Only one of 'path', 'credentialsString', 'token' or 'apiKey' is allowed."
logNoTokenGenerationMeansPassed = "Invalid credentials. Any one of 'path', 'credentialsString', 'token' or 'apiKey' is required."
logInvalidApiKey = "Invalid credentials. Api key is invalid."
logEmptyRoles = "Invalid credentials. Roles can not be empty."
logEmptyOrNullRoleInRoles = "Invalid credentials. Role can not be null or empty in roles at index %v."
)
123 changes: 123 additions & 0 deletions common/config/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package config

import (
"fmt"
"net/url"
"strings"

skyflowError "github.com/skyflowapi/skyflow-go/common/errors"
"github.com/skyflowapi/skyflow-go/common/logger"
)

const (
apiKeyPrefix = "sky-"
apiKeyLength = 42
httpProtocol = "http"
httpsProtocol = "https"
)

// Validator validates the shared configuration types. The prefixes identify
// the SDK on whose behalf it validates, so messages carry that SDK's
// name/version.
type Validator struct {
SDKPrefix string // prepended to error messages, e.g. "Skyflow Go SDK v2.1.0"
LogPrefix string // prepended to log lines, e.g. "[ Skyflow Go SDK v2.1.0 ] "
}

func NewValidator(sdkPrefix, logPrefix string) Validator {
return Validator{SDKPrefix: sdkPrefix, LogPrefix: logPrefix}
}

func (v Validator) errMsg(base string) string { return v.SDKPrefix + base }
func (v Validator) logMsg(base string) string { return v.LogPrefix + base }

func (v Validator) invalid(logBase, errBase string) *skyflowError.SkyflowError {
logger.Error(v.logMsg(logBase))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, v.errMsg(errBase))
}

// ValidateVaultConfig checks a vault config used to register a vault.
func (v Validator) ValidateVaultConfig(vaultConfig VaultConfig) *skyflowError.SkyflowError {
if vaultConfig.VaultId == "" {
return v.invalid(logVaultIdIsRequired, errInvalidVaultId)
}
baseVaultUrl := vaultConfig.BaseVaultUrl
if vaultConfig.BaseVaultURL != "" {
logger.Warn(v.logMsg(logDeprecatedFieldBaseVaultUrl))
if baseVaultUrl == "" {
baseVaultUrl = vaultConfig.BaseVaultURL
}
}
if baseVaultUrl == "" {
if vaultConfig.ClusterId == "" {
return v.invalid(logClusterIdIsRequired, errInvalidClusterId)
}
} else if !isValidHTTPURL(baseVaultUrl) {
return v.invalid(logVaultUrlIsInvalid, errInvalidVaultUrl)
}
return nil
}

// ValidateUpdateVaultConfig checks a vault config used to update a registered vault.
func (v Validator) ValidateUpdateVaultConfig(vaultConfig VaultConfig) *skyflowError.SkyflowError {
if vaultConfig.VaultId == "" {
return v.invalid(logVaultIdIsRequired, errInvalidVaultId)
}
return nil
}

// ValidateCredentials checks that exactly one credential source is set and
// that api key and roles, when present, are well formed.
func (v Validator) ValidateCredentials(credentials Credentials) *skyflowError.SkyflowError {
credPresent := 0
for _, set := range []bool{
credentials.Path != "",
credentials.CredentialsString != "",
credentials.Token != "",
credentials.ApiKey != "",
} {
if set {
credPresent++
}
}
if credPresent > 1 {
return v.invalid(logMultipleTokenGenerationMeansPassed, errMultipleTokenGenerationMeansPassed)
} else if credPresent < 1 {
return v.invalid(logNoTokenGenerationMeansPassed, errNoTokenGenerationMeansPassed)
}

if credentials.ApiKey != "" {
if len(credentials.ApiKey) != apiKeyLength || !strings.Contains(credentials.ApiKey, apiKeyPrefix) {
return v.invalid(logInvalidApiKey, errInvalidApiKey)
}
}

if credentials.Roles != nil {
if len(credentials.Roles) == 0 {
return v.invalid(logEmptyRoles, errEmptyRoles)
}
for index, role := range credentials.Roles {
if role == "" {
logger.Error(fmt.Sprintf(v.logMsg(logEmptyOrNullRoleInRoles), index))
return skyflowError.NewSkyflowError(skyflowError.INVALID_INPUT_CODE, v.errMsg(errEmptyRoleInRoles))
}
}
}
return nil
}

// IsCredentialsEmpty reports whether no credential source is set.
func IsCredentialsEmpty(c Credentials) bool {
return c.Path == "" && c.CredentialsString == "" && c.Token == "" && c.ApiKey == ""
}

func isValidHTTPURL(raw string) bool {
u, err := url.Parse(raw)
if err != nil {
return false
}
if u.Scheme != httpProtocol && u.Scheme != httpsProtocol {
return false
}
return u.Host != ""
}
14 changes: 14 additions & 0 deletions common/errors/error_codes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package errors

// ErrorCodesEnum - Enum defines the list of error codes/categorization of errors in Skyflow.
type ErrorCodesEnum string

// Defining the values of Error code Enum
const (
// SERVER - Represents server side error
SERVER ErrorCodesEnum = "500"
// INVALID_INPUT - Input passed was not invalid format
INVALID_INPUT_CODE ErrorCodesEnum = "400"
INVALID_INDEX ErrorCodesEnum = "404"
PARTIAL ErrorCodesEnum = "404"
)
Loading
Loading