mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 13:27:22 +00:00
feat: migrated the operator code to v4
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"errors"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
infisicalSdk "github.com/infisical/go-sdk"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string) (string, error) {
|
||||
|
||||
if autoCreateServiceAccountToken {
|
||||
restClient, err := GetRestClientFromClient()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get REST client: %w", err)
|
||||
}
|
||||
|
||||
tokenRequest := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
ExpirationSeconds: ptr.Int64(600), // 10 minutes. the token only needs to be valid for when we do the initial k8s login.
|
||||
},
|
||||
}
|
||||
|
||||
if len(serviceAccountTokenAudiences) > 0 {
|
||||
// Conditionally add the audiences if they are specified.
|
||||
// Failing to do this causes a default audience to be used, which is not what we want if the user doesn't specify any.
|
||||
tokenRequest.Spec.Audiences = serviceAccountTokenAudiences
|
||||
}
|
||||
|
||||
result := &authenticationv1.TokenRequest{}
|
||||
err = restClient.
|
||||
Post().
|
||||
Namespace(namespace).
|
||||
Resource("serviceaccounts").
|
||||
Name(serviceAccountName).
|
||||
SubResource("token").
|
||||
Body(tokenRequest).
|
||||
Do(context.Background()).
|
||||
Into(result)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create token: %w", err)
|
||||
}
|
||||
|
||||
return result.Status.Token, nil
|
||||
}
|
||||
|
||||
serviceAccount := &corev1.ServiceAccount{}
|
||||
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(serviceAccount.Secrets) == 0 {
|
||||
return "", fmt.Errorf("no secrets found for service account %s", serviceAccountName)
|
||||
}
|
||||
|
||||
secretName := serviceAccount.Secrets[0].Name
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := secret.Data["token"]
|
||||
|
||||
return string(token), nil
|
||||
}
|
||||
|
||||
type AuthStrategyType string
|
||||
|
||||
var AuthStrategy = struct {
|
||||
SERVICE_TOKEN AuthStrategyType
|
||||
SERVICE_ACCOUNT AuthStrategyType
|
||||
UNIVERSAL_MACHINE_IDENTITY AuthStrategyType
|
||||
KUBERNETES_MACHINE_IDENTITY AuthStrategyType
|
||||
AWS_IAM_MACHINE_IDENTITY AuthStrategyType
|
||||
AZURE_MACHINE_IDENTITY AuthStrategyType
|
||||
GCP_ID_TOKEN_MACHINE_IDENTITY AuthStrategyType
|
||||
GCP_IAM_MACHINE_IDENTITY AuthStrategyType
|
||||
LDAP_MACHINE_IDENTITY AuthStrategyType
|
||||
}{
|
||||
SERVICE_TOKEN: "SERVICE_TOKEN",
|
||||
SERVICE_ACCOUNT: "SERVICE_ACCOUNT",
|
||||
UNIVERSAL_MACHINE_IDENTITY: "UNIVERSAL_MACHINE_IDENTITY",
|
||||
KUBERNETES_MACHINE_IDENTITY: "KUBERNETES_AUTH_MACHINE_IDENTITY",
|
||||
AWS_IAM_MACHINE_IDENTITY: "AWS_IAM_MACHINE_IDENTITY",
|
||||
AZURE_MACHINE_IDENTITY: "AZURE_MACHINE_IDENTITY",
|
||||
GCP_ID_TOKEN_MACHINE_IDENTITY: "GCP_ID_TOKEN_MACHINE_IDENTITY",
|
||||
GCP_IAM_MACHINE_IDENTITY: "GCP_IAM_MACHINE_IDENTITY",
|
||||
LDAP_MACHINE_IDENTITY: "LDAP_MACHINE_IDENTITY",
|
||||
}
|
||||
|
||||
type SecretCrdType string
|
||||
|
||||
var SecretCrd = struct {
|
||||
INFISICAL_SECRET SecretCrdType
|
||||
INFISICAL_PUSH_SECRET SecretCrdType
|
||||
INFISICAL_DYNAMIC_SECRET SecretCrdType
|
||||
}{
|
||||
INFISICAL_SECRET: "INFISICAL_SECRET",
|
||||
INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET",
|
||||
INFISICAL_DYNAMIC_SECRET: "INFISICAL_DYNAMIC_SECRET",
|
||||
}
|
||||
|
||||
type SecretAuthInput struct {
|
||||
Secret interface{}
|
||||
Type SecretCrdType
|
||||
}
|
||||
|
||||
type AuthenticationDetails struct {
|
||||
AuthStrategy AuthStrategyType
|
||||
MachineIdentityScope v1alpha1.MachineIdentityScopeInWorkspace // This will only be set if a machine identity auth method is used (e.g. UniversalAuth or KubernetesAuth, etc.)
|
||||
IsMachineIdentityAuth bool
|
||||
SecretType SecretCrdType
|
||||
}
|
||||
|
||||
var ErrAuthNotApplicable = errors.New("authentication not applicable")
|
||||
|
||||
func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
|
||||
var universalAuthSpec v1alpha1.UniversalAuthDetails
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
universalAuthSpec = infisicalSecret.Spec.Authentication.UniversalAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
universalAuthSpec = v1alpha1.UniversalAuthDetails{
|
||||
CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
universalAuthSpec = v1alpha1.UniversalAuthDetails{
|
||||
CredentialsRef: infisicalDynamicSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
|
||||
SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace,
|
||||
SecretName: universalAuthSpec.CredentialsRef.SecretName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err)
|
||||
}
|
||||
|
||||
if universalAuthKubeSecret.ClientId == "" && universalAuthKubeSecret.ClientSecret == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err = infisicalClient.Auth().UniversalAuthLogin(universalAuthKubeSecret.ClientId, universalAuthKubeSecret.ClientSecret)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with machine identity credentials [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.UNIVERSAL_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: universalAuthSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
|
||||
var ldapAuthSpec v1alpha1.LdapAuthDetails
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
ldapAuthSpec = infisicalSecret.Spec.Authentication.LdapAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
ldapAuthSpec = v1alpha1.LdapAuthDetails{
|
||||
CredentialsRef: infisicalPushSecret.Spec.Authentication.LdapAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.LdapAuth.IdentityID,
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
ldapAuthSpec = v1alpha1.LdapAuthDetails{
|
||||
CredentialsRef: infisicalDynamicSecret.Spec.Authentication.LdapAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.LdapAuth.IdentityID,
|
||||
}
|
||||
}
|
||||
|
||||
ldapAuthKubeSecret, err := GetInfisicalLdapAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
|
||||
SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace,
|
||||
SecretName: ldapAuthSpec.CredentialsRef.SecretName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err)
|
||||
}
|
||||
|
||||
if ldapAuthKubeSecret.Username == "" && ldapAuthKubeSecret.Password == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err = infisicalClient.Auth().LdapAuthLogin(ldapAuthSpec.IdentityID, ldapAuthKubeSecret.Username, ldapAuthKubeSecret.Password)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with machine identity credentials [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.LDAP_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: ldapAuthSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
kubernetesAuthSpec = infisicalSecret.Spec.Authentication.KubernetesAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.KubernetesAuth.IdentityID,
|
||||
ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{
|
||||
Namespace: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace,
|
||||
Name: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name,
|
||||
},
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
AutoCreateServiceAccountToken: infisicalPushSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken,
|
||||
ServiceAccountTokenAudiences: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences,
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.IdentityID,
|
||||
ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{
|
||||
Namespace: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace,
|
||||
Name: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name,
|
||||
},
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
AutoCreateServiceAccountToken: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken,
|
||||
ServiceAccountTokenAudiences: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences,
|
||||
}
|
||||
}
|
||||
|
||||
if kubernetesAuthSpec.IdentityID == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
serviceAccountToken, err := GetServiceAccountToken(
|
||||
reconcilerClient,
|
||||
kubernetesAuthSpec.ServiceAccountRef.Namespace,
|
||||
kubernetesAuthSpec.ServiceAccountRef.Name,
|
||||
kubernetesAuthSpec.AutoCreateServiceAccountToken,
|
||||
kubernetesAuthSpec.ServiceAccountTokenAudiences,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to get service account token [err=%s]", err)
|
||||
}
|
||||
|
||||
_, err = infisicalClient.Auth().KubernetesRawServiceAccountTokenLogin(kubernetesAuthSpec.IdentityID, serviceAccountToken)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with Kubernetes native auth [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.KUBERNETES_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: kubernetesAuthSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{}
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
|
||||
awsIamAuthSpec = infisicalSecret.Spec.Authentication.AwsIamAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if awsIamAuthSpec.IdentityID == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err := infisicalClient.Auth().AwsIamAuthLogin(awsIamAuthSpec.IdentityID)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with AWS IAM auth [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.AWS_IAM_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: awsIamAuthSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
azureAuthSpec := v1alpha1.AzureAuthDetails{}
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
|
||||
azureAuthSpec = infisicalSecret.Spec.Authentication.AzureAuth
|
||||
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
azureAuthSpec = v1alpha1.AzureAuthDetails{
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.AzureAuth.IdentityID,
|
||||
Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
azureAuthSpec = v1alpha1.AzureAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.AzureAuth.IdentityID,
|
||||
Resource: infisicalDynamicSecret.Spec.Authentication.AzureAuth.Resource,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if azureAuthSpec.IdentityID == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err := infisicalClient.Auth().AzureAuthLogin(azureAuthSpec.IdentityID, azureAuthSpec.Resource) // If resource is empty(""), it will default to "https://management.azure.com/" in the SDK.
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with Azure auth [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.AZURE_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: azureAuthSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{}
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
|
||||
gcpIdTokenSpec = infisicalSecret.Spec.Authentication.GcpIdTokenAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if gcpIdTokenSpec.IdentityID == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err := infisicalClient.Auth().GcpIdTokenAuthLogin(gcpIdTokenSpec.IdentityID)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP Id Token auth [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: gcpIdTokenSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
|
||||
gcpIamSpec := v1alpha1.GcpIamAuthDetails{}
|
||||
|
||||
switch secretCrd.Type {
|
||||
case SecretCrd.INFISICAL_SECRET:
|
||||
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
|
||||
}
|
||||
|
||||
gcpIamSpec = infisicalSecret.Spec.Authentication.GcpIamAuth
|
||||
case SecretCrd.INFISICAL_PUSH_SECRET:
|
||||
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
|
||||
}
|
||||
|
||||
gcpIamSpec = v1alpha1.GcpIamAuthDetails{
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.GcpIamAuth.IdentityID,
|
||||
ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
gcpIamSpec = v1alpha1.GcpIamAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.IdentityID,
|
||||
ServiceAccountKeyFilePath: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" {
|
||||
return AuthenticationDetails{}, ErrAuthNotApplicable
|
||||
}
|
||||
|
||||
_, err := infisicalClient.Auth().GcpIamAuthLogin(gcpIamSpec.IdentityID, gcpIamSpec.ServiceAccountKeyFilePath)
|
||||
if err != nil {
|
||||
return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP IAM auth [err=%s]", err)
|
||||
}
|
||||
|
||||
return AuthenticationDetails{
|
||||
AuthStrategy: AuthStrategy.GCP_IAM_MACHINE_IDENTITY,
|
||||
MachineIdentityScope: gcpIamSpec.SecretsScope,
|
||||
IsMachineIdentityAuth: true,
|
||||
SecretType: secretCrd.Type,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ConvertIntervalToDuration(resyncInterval *string) (time.Duration, error) {
|
||||
|
||||
if resyncInterval == nil || *resyncInterval == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
length := len(*resyncInterval)
|
||||
if length < 2 {
|
||||
return 0, fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
unit := (*resyncInterval)[length-1:]
|
||||
numberPart := (*resyncInterval)[:length-1]
|
||||
|
||||
number, err := strconv.Atoi(numberPart)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch unit {
|
||||
case "s":
|
||||
if number < 5 {
|
||||
return 0, fmt.Errorf("resync interval must be at least 5 seconds")
|
||||
}
|
||||
return time.Duration(number) * time.Second, nil
|
||||
case "m":
|
||||
return time.Duration(number) * time.Minute, nil
|
||||
case "h":
|
||||
return time.Duration(number) * time.Hour, nil
|
||||
case "d":
|
||||
return time.Duration(number) * 24 * time.Hour, nil
|
||||
case "w":
|
||||
return time.Duration(number) * 7 * 24 * time.Hour, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid time unit")
|
||||
}
|
||||
}
|
||||
|
||||
func AppendAPIEndpoint(address string) string {
|
||||
if strings.HasSuffix(address, "/api") {
|
||||
return address
|
||||
}
|
||||
if address[len(address)-1] == '/' {
|
||||
return address + "api"
|
||||
}
|
||||
return address + "/api"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId"
|
||||
const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret"
|
||||
|
||||
func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.Secret, error) {
|
||||
kubeSecret := &corev1.Secret{}
|
||||
err := reconcilerClient.Get(ctx, namespacedName, kubeSecret)
|
||||
if err != nil {
|
||||
kubeSecret = nil
|
||||
}
|
||||
|
||||
return kubeSecret, err
|
||||
}
|
||||
|
||||
func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.ConfigMap, error) {
|
||||
kubeConfigMap := &corev1.ConfigMap{}
|
||||
err := reconcilerClient.Get(ctx, namespacedName, kubeConfigMap)
|
||||
if err != nil {
|
||||
kubeConfigMap = nil
|
||||
}
|
||||
|
||||
return kubeConfigMap, err
|
||||
}
|
||||
|
||||
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.MachineIdentityDetails, err error) {
|
||||
|
||||
universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
|
||||
Namespace: universalAuthRef.SecretNamespace,
|
||||
Name: universalAuthRef.SecretName,
|
||||
// Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace,
|
||||
// Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName,
|
||||
})
|
||||
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return model.MachineIdentityDetails{}, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return model.MachineIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
|
||||
}
|
||||
|
||||
clientIdFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_ID]
|
||||
clientSecretFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET]
|
||||
|
||||
return model.MachineIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil
|
||||
|
||||
}
|
||||
|
||||
func getKubeClusterConfig() (*rest.Config, error) {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
configOverrides := &clientcmd.ConfigOverrides{}
|
||||
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
|
||||
return kubeConfig.ClientConfig()
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func GetRestClientFromClient() (rest.Interface, error) {
|
||||
|
||||
config, err := getKubeClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clientset.CoreV1().RESTClient(), nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
infisicalSdk "github.com/infisical/go-sdk"
|
||||
)
|
||||
|
||||
type ResourceVariables struct {
|
||||
InfisicalClient infisicalSdk.InfisicalClientInterface
|
||||
CancelCtx context.CancelFunc
|
||||
AuthDetails AuthenticationDetails
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
"github.com/go-resty/resty/v2"
|
||||
infisical "github.com/infisical/go-sdk"
|
||||
)
|
||||
|
||||
type DecodedSymmetricEncryptionDetails = struct {
|
||||
Cipher []byte
|
||||
IV []byte
|
||||
Tag []byte
|
||||
Key []byte
|
||||
}
|
||||
|
||||
func VerifyServiceToken(serviceToken string) (string, error) {
|
||||
serviceTokenParts := strings.SplitN(serviceToken, ".", 4)
|
||||
if len(serviceTokenParts) < 4 {
|
||||
return "", fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
}
|
||||
|
||||
serviceToken = fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
|
||||
return serviceToken, nil
|
||||
}
|
||||
|
||||
func GetServiceTokenDetails(infisicalToken string) (api.GetServiceTokenDetailsResponse, error) {
|
||||
serviceTokenParts := strings.SplitN(infisicalToken, ".", 4)
|
||||
if len(serviceTokenParts) < 4 {
|
||||
return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
}
|
||||
|
||||
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
|
||||
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(serviceToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
|
||||
if err != nil {
|
||||
return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to get service token details. [err=%v]", err)
|
||||
}
|
||||
|
||||
return serviceTokenDetails, nil
|
||||
}
|
||||
|
||||
func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalClientInterface, secretScope v1alpha1.MachineIdentityScopeInWorkspace) ([]model.SingleEnvironmentVariable, error) {
|
||||
|
||||
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
|
||||
ProjectSlug: secretScope.ProjectSlug,
|
||||
Environment: secretScope.EnvSlug,
|
||||
Recursive: secretScope.Recursive,
|
||||
SecretPath: secretScope.SecretsPath,
|
||||
IncludeImports: true,
|
||||
ExpandSecretReferences: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get secrets. [err=%v]", err)
|
||||
}
|
||||
|
||||
var environmentVariables []model.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range secrets {
|
||||
|
||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||
Key: secret.SecretKey,
|
||||
Value: secret.SecretValue,
|
||||
Type: secret.Type,
|
||||
ID: secret.ID,
|
||||
SecretPath: secret.SecretPath,
|
||||
})
|
||||
}
|
||||
|
||||
return environmentVariables, nil
|
||||
}
|
||||
|
||||
func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, error) {
|
||||
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
|
||||
if len(serviceTokenParts) < 4 {
|
||||
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
}
|
||||
|
||||
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
|
||||
|
||||
httpClient := resty.New()
|
||||
|
||||
httpClient.SetAuthToken(serviceToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
|
||||
}
|
||||
|
||||
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
|
||||
ProjectID: serviceTokenDetails.Workspace,
|
||||
Environment: envSlug,
|
||||
Recursive: recursive,
|
||||
SecretPath: secretPath,
|
||||
IncludeImports: true,
|
||||
ExpandSecretReferences: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var environmentVariables []model.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range secrets {
|
||||
|
||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||
Key: secret.SecretKey,
|
||||
Value: secret.SecretValue,
|
||||
Type: secret.Type,
|
||||
ID: secret.ID,
|
||||
SecretPath: secret.SecretPath,
|
||||
})
|
||||
}
|
||||
|
||||
return environmentVariables, nil
|
||||
|
||||
}
|
||||
|
||||
// Fetches plaintext secrets from an API endpoint using a service account.
|
||||
// The function fetches the service account details and keys, decrypts the workspace key, fetches the encrypted secrets for the specified project and environment, and decrypts the secrets using the decrypted workspace key.
|
||||
// Returns the plaintext secrets, encrypted secrets response, and any errors that occurred during the process.
|
||||
func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string) ([]model.SingleEnvironmentVariable, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(serviceAccountCreds.AccessKey).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
serviceAccountDetails, err := api.CallGetServiceTokenAccountDetailsV2(httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account details. [err=%v]", err)
|
||||
}
|
||||
|
||||
serviceAccountKeys, err := api.CallGetServiceAccountKeysV2(httpClient, api.GetServiceAccountKeysRequest{ServiceAccountId: serviceAccountDetails.ServiceAccount.ID})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account key details. [err=%v]", err)
|
||||
}
|
||||
|
||||
// find key for requested project
|
||||
var workspaceServiceAccountKey api.ServiceAccountKey
|
||||
for _, serviceAccountKey := range serviceAccountKeys.ServiceAccountKeys {
|
||||
if serviceAccountKey.Workspace == projectId {
|
||||
workspaceServiceAccountKey = serviceAccountKey
|
||||
}
|
||||
}
|
||||
|
||||
if workspaceServiceAccountKey.ID == "" || workspaceServiceAccountKey.EncryptedKey == "" || workspaceServiceAccountKey.Nonce == "" || serviceAccountCreds.PublicKey == "" || serviceAccountCreds.PrivateKey == "" {
|
||||
return nil, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err)
|
||||
}
|
||||
|
||||
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
|
||||
ProjectID: projectId,
|
||||
Environment: environmentName,
|
||||
Recursive: false,
|
||||
SecretPath: "/",
|
||||
IncludeImports: true,
|
||||
ExpandSecretReferences: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var environmentVariables []model.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range secrets {
|
||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||
Key: secret.SecretKey,
|
||||
Value: secret.SecretValue,
|
||||
Type: secret.Type,
|
||||
ID: secret.ID,
|
||||
SecretPath: secret.SecretPath,
|
||||
})
|
||||
}
|
||||
|
||||
return environmentVariables, nil
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, error) {
|
||||
length := len(resyncInterval)
|
||||
if length < 2 {
|
||||
return 0, fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
unit := resyncInterval[length-1:]
|
||||
numberPart := resyncInterval[:length-1]
|
||||
|
||||
number, err := strconv.Atoi(numberPart)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch unit {
|
||||
case "s":
|
||||
if number < 5 {
|
||||
return 0, fmt.Errorf("resync interval must be at least 5 seconds")
|
||||
}
|
||||
return time.Duration(number) * time.Second, nil
|
||||
case "m":
|
||||
return time.Duration(number) * time.Minute, nil
|
||||
case "h":
|
||||
return time.Duration(number) * time.Hour, nil
|
||||
case "d":
|
||||
return time.Duration(number) * 24 * time.Hour, nil
|
||||
case "w":
|
||||
return time.Duration(number) * 7 * 24 * time.Hour, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid time unit")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func GetProjectByID(accessToken string, projectId string) (model.Project, error) {
|
||||
|
||||
httpClient := resty.New()
|
||||
httpClient.
|
||||
SetAuthScheme("Bearer").
|
||||
SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
projectDetails, err := api.CallGetProjectByID(httpClient, api.GetProjectByIDRequest{
|
||||
ProjectID: projectId,
|
||||
})
|
||||
if err != nil {
|
||||
return model.Project{}, fmt.Errorf("unable to get project by slug. [err=%v]", err)
|
||||
}
|
||||
|
||||
return projectDetails.Project, nil
|
||||
}
|
||||
Reference in New Issue
Block a user