mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-23 03:03:05 +00:00
Compare commits
48 Commits
rate-limit
...
daniel/cli
Author | SHA1 | Date | |
---|---|---|---|
85f257b4db | |||
18d7a14e3f | |||
a975fbd8a4 | |||
3a6ec3717b | |||
a4a961996b | |||
71575b1d2e | |||
51f164c399 | |||
0baea4c5fd | |||
dde24d4c71 | |||
8f1e662688 | |||
dcbbb67f03 | |||
c0fb3c905e | |||
18b0766d96 | |||
b423696630 | |||
bf60489fde | |||
85ea6d2585 | |||
a97737ab90 | |||
3793858f0a | |||
66c48fbff8 | |||
b6b040375b | |||
9ad5e082e2 | |||
f1805811aa | |||
b135258cce | |||
a651de53d1 | |||
7d0a535f46 | |||
c4e3dd84e3 | |||
9193f13970 | |||
016f22c295 | |||
4d7182c9b1 | |||
6ea7b04efa | |||
3981d61853 | |||
3d391b4e2d | |||
4123177133 | |||
4d61188d0f | |||
fa33f35fcd | |||
13629223fb | |||
ba1f8f4564 | |||
e26df005c2 | |||
aca9b47f82 | |||
a16ce8899b | |||
b61511d100 | |||
a945bdfc4c | |||
3f6999b2e3 | |||
9128461409 | |||
893235c40f | |||
e0f655ae30 | |||
93aeca3a38 | |||
1edebdf8a5 |
@ -7,10 +7,10 @@ const prompt = promptSync({ sigint: true });
|
||||
|
||||
const migrationName = prompt("Enter name for migration: ");
|
||||
|
||||
// Remove spaces from migration name and replace with hyphens
|
||||
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
|
||||
|
||||
execSync(
|
||||
`npx knex migrate:make --knexfile ${path.join(
|
||||
__dirname,
|
||||
"../src/db/knexfile.ts"
|
||||
)} -x ts ${migrationName}`,
|
||||
`npx knex migrate:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${formattedMigrationName}`,
|
||||
{ stdio: "inherit" }
|
||||
);
|
||||
|
@ -0,0 +1,111 @@
|
||||
import { Knex } from "knex";
|
||||
import { z } from "zod";
|
||||
|
||||
import { TableName, TOrgMemberships } from "../schemas";
|
||||
|
||||
const validateOrgMembership = (membershipToValidate: TOrgMemberships, firstMembership: TOrgMemberships) => {
|
||||
const firstOrgId = firstMembership.orgId;
|
||||
const firstUserId = firstMembership.userId;
|
||||
|
||||
if (membershipToValidate.id === firstMembership.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (membershipToValidate.inviteEmail !== firstMembership.inviteEmail) {
|
||||
throw new Error(`Invite emails are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.orgId !== firstMembership.orgId) {
|
||||
throw new Error(`OrgIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.role !== firstMembership.role) {
|
||||
throw new Error(`Roles are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.roleId !== firstMembership.roleId) {
|
||||
throw new Error(`RoleIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.status !== firstMembership.status) {
|
||||
throw new Error(`Statuses are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.userId !== firstMembership.userId) {
|
||||
throw new Error(`UserIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
};
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const RowSchema = z.object({
|
||||
userId: z.string(),
|
||||
orgId: z.string(),
|
||||
cnt: z.string()
|
||||
});
|
||||
|
||||
// Transactional find and delete duplicate rows
|
||||
await knex.transaction(async (tx) => {
|
||||
const duplicateRows = await tx(TableName.OrgMembership)
|
||||
.select("userId", "orgId") // Select the userId and orgId so we can group by them
|
||||
.count("* as cnt") // Count the number of rows for each userId and orgId, so we can make sure there are more than 1 row (a duplicate)
|
||||
.groupBy("userId", "orgId")
|
||||
.havingRaw("count(*) > ?", [1]); // Using havingRaw for direct SQL expressions
|
||||
|
||||
// Parse the rows to ensure they are in the correct format, and for type safety
|
||||
const parsedRows = RowSchema.array().parse(duplicateRows);
|
||||
|
||||
// For each of the duplicate rows, loop through and find the actual memberships to delete
|
||||
for (const row of parsedRows) {
|
||||
const count = Number(row.cnt);
|
||||
|
||||
// An extra check to ensure that the count is actually a number, and the number is greater than 2
|
||||
if (typeof count !== "number" || count < 2) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find all the organization memberships that have the same userId and orgId
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const rowsToDelete = await tx(TableName.OrgMembership).where({
|
||||
userId: row.userId,
|
||||
orgId: row.orgId
|
||||
});
|
||||
|
||||
// Ensure that all the rows have exactly the same value, except id, createdAt, updatedAt
|
||||
for (const rowToDelete of rowsToDelete) {
|
||||
validateOrgMembership(rowToDelete, rowsToDelete[0]);
|
||||
}
|
||||
|
||||
// Find the row with the latest createdAt, which we will keep
|
||||
|
||||
let lowestCreatedAt: number | null = null;
|
||||
let latestCreatedRow: TOrgMemberships | null = null;
|
||||
|
||||
for (const rowToDelete of rowsToDelete) {
|
||||
if (lowestCreatedAt === null || rowToDelete.createdAt.getTime() < lowestCreatedAt) {
|
||||
lowestCreatedAt = rowToDelete.createdAt.getTime();
|
||||
latestCreatedRow = rowToDelete;
|
||||
}
|
||||
}
|
||||
if (!latestCreatedRow) {
|
||||
throw new Error("Failed to find last created membership");
|
||||
}
|
||||
|
||||
// Filter out the latest row from the rows to delete
|
||||
const membershipIdsToDelete = rowsToDelete.map((r) => r.id).filter((id) => id !== latestCreatedRow!.id);
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const numberOfRowsDeleted = await tx(TableName.OrgMembership).whereIn("id", membershipIdsToDelete).delete();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`Deleted ${numberOfRowsDeleted} duplicate organization memberships for ${row.userId} and ${row.orgId}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||
table.unique(["userId", "orgId"]);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||
table.dropUnique(["userId", "orgId"]);
|
||||
});
|
||||
}
|
@ -411,7 +411,12 @@ export const registerRoutes = async (
|
||||
folderDAL
|
||||
});
|
||||
|
||||
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDAL });
|
||||
const projectRoleService = projectRoleServiceFactory({
|
||||
permissionService,
|
||||
projectRoleDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
});
|
||||
|
||||
const snapshotService = secretSnapshotServiceFactory({
|
||||
permissionService,
|
||||
|
@ -14,16 +14,25 @@ import {
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
||||
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { TProjectRoleDALFactory } from "./project-role-dal";
|
||||
|
||||
type TProjectRoleServiceFactoryDep = {
|
||||
projectRoleDAL: TProjectRoleDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
|
||||
identityProjectMembershipRoleDAL: TIdentityProjectMembershipRoleDALFactory;
|
||||
projectUserMembershipRoleDAL: TProjectUserMembershipRoleDALFactory;
|
||||
};
|
||||
|
||||
export type TProjectRoleServiceFactory = ReturnType<typeof projectRoleServiceFactory>;
|
||||
|
||||
export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => {
|
||||
export const projectRoleServiceFactory = ({
|
||||
projectRoleDAL,
|
||||
permissionService,
|
||||
identityProjectMembershipRoleDAL,
|
||||
projectUserMembershipRoleDAL
|
||||
}: TProjectRoleServiceFactoryDep) => {
|
||||
const createRole = async (
|
||||
actor: ActorType,
|
||||
actorId: string,
|
||||
@ -96,8 +105,25 @@ export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }:
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
|
||||
|
||||
const identityRole = await identityProjectMembershipRoleDAL.findOne({ customRoleId: roleId });
|
||||
const projectUserRole = await projectUserMembershipRoleDAL.findOne({ customRoleId: roleId });
|
||||
|
||||
if (identityRole) {
|
||||
throw new BadRequestError({
|
||||
message: "The role is assigned to one or more identities. Make sure to unassign them before deleting the role.",
|
||||
name: "Delete role"
|
||||
});
|
||||
}
|
||||
if (projectUserRole) {
|
||||
throw new BadRequestError({
|
||||
message: "The role is assigned to one or more users. Make sure to unassign them before deleting the role.",
|
||||
name: "Delete role"
|
||||
});
|
||||
}
|
||||
|
||||
const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId });
|
||||
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
|
||||
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Delete role" });
|
||||
|
||||
return deletedRole;
|
||||
};
|
||||
|
@ -528,6 +528,7 @@ type GetRawSecretsV3Request struct {
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
IncludeImport bool `json:"include_imports"`
|
||||
Recursive bool `json:"recursive"`
|
||||
}
|
||||
|
||||
type GetRawSecretsV3Response struct {
|
||||
|
@ -479,7 +479,7 @@ func (tm *AgentManager) GetToken() string {
|
||||
|
||||
// Fetches a new access token using client credentials
|
||||
func (tm *AgentManager) FetchNewAccessToken() error {
|
||||
clientID := os.Getenv("INFISICAL_UNIVERSAL_AUTH_CLIENT_ID")
|
||||
clientID := os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME)
|
||||
if clientID == "" {
|
||||
clientIDAsByte, err := ReadFile(tm.clientIdPath)
|
||||
if err != nil {
|
||||
@ -509,7 +509,7 @@ func (tm *AgentManager) FetchNewAccessToken() error {
|
||||
// save as cache in memory
|
||||
tm.cachedClientSecret = clientSecret
|
||||
|
||||
err, loginResponse := universalAuthLogin(clientID, clientSecret)
|
||||
loginResponse, err := util.UniversalAuthLogin(clientID, clientSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -725,20 +725,6 @@ func (tm *AgentManager) MonitorSecretChanges(secretTemplate Template, templateId
|
||||
}
|
||||
}
|
||||
|
||||
func universalAuthLogin(clientId string, clientSecret string) (error, api.UniversalAuthLoginResponse) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
|
||||
tokenResponse, err := api.CallUniversalAuthLogin(httpClient, api.UniversalAuthLoginRequest{ClientId: clientId, ClientSecret: clientSecret})
|
||||
if err != nil {
|
||||
return err, api.UniversalAuthLoginResponse{}
|
||||
}
|
||||
|
||||
return nil, tokenResponse
|
||||
}
|
||||
|
||||
// runCmd represents the run command
|
||||
var agentCmd = &cobra.Command{
|
||||
Example: `
|
||||
|
@ -44,6 +44,11 @@ var exportCmd = &cobra.Command{
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
includeImports, err := cmd.Flags().GetBool("include-imports")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
@ -59,8 +64,7 @@ var exportCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
@ -75,7 +79,21 @@ var exportCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId, SecretsPath: secretsPath}, "")
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
TagSlugs: tagSlugs,
|
||||
WorkspaceId: projectId,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(request, "")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
@ -88,9 +106,16 @@ var exportCmd = &cobra.Command{
|
||||
|
||||
var output string
|
||||
if shouldExpandSecrets {
|
||||
secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{
|
||||
InfisicalToken: infisicalToken,
|
||||
}, "")
|
||||
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
secrets = util.FilterSecretsByTag(secrets, tagSlugs)
|
||||
output, err = formatEnvs(secrets, format)
|
||||
@ -110,6 +135,7 @@ func init() {
|
||||
exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)")
|
||||
exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||
exportCmd.Flags().Bool("include-imports", true, "Imported linked secrets")
|
||||
exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs")
|
||||
exportCmd.Flags().String("projectId", "", "manually set the projectId to fetch secrets from")
|
||||
|
@ -36,18 +36,33 @@ var getCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
foldersPath, err := cmd.Flags().GetString("path")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
folders, err := util.GetAllFolders(models.GetAllFoldersParameters{Environment: environmentName, InfisicalToken: infisicalToken, FoldersPath: foldersPath})
|
||||
request := models.GetAllFoldersParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
FoldersPath: foldersPath,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
folders, err := util.GetAllFolders(request)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to get folders")
|
||||
}
|
||||
|
@ -55,95 +55,157 @@ var loginCmd = &cobra.Command{
|
||||
Short: "Login into your Infisical account",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
// if the key can't be found or there is an error getting current credentials from key ring, allow them to override
|
||||
if err != nil && (strings.Contains(err.Error(), "we couldn't find your logged in details")) {
|
||||
log.Debug().Err(err)
|
||||
} else if err != nil {
|
||||
|
||||
loginMethod, err := cmd.Flags().GetString("method")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
plainOutput, err := cmd.Flags().GetBool("plain")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired && len(currentLoggedInUserDetails.UserCredentials.PrivateKey) != 0 {
|
||||
shouldOverride, err := userLoginMenu(currentLoggedInUserDetails.UserCredentials.Email)
|
||||
if err != nil {
|
||||
if loginMethod != "user" && loginMethod != "universal-auth" {
|
||||
util.PrintErrorMessageAndExit("Invalid login method. Please use either 'user' or 'universal-auth'")
|
||||
}
|
||||
|
||||
if loginMethod == "user" {
|
||||
|
||||
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
// if the key can't be found or there is an error getting current credentials from key ring, allow them to override
|
||||
if err != nil && (strings.Contains(err.Error(), "we couldn't find your logged in details")) {
|
||||
log.Debug().Err(err)
|
||||
} else if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if !shouldOverride {
|
||||
return
|
||||
if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired && len(currentLoggedInUserDetails.UserCredentials.PrivateKey) != 0 {
|
||||
shouldOverride, err := userLoginMenu(currentLoggedInUserDetails.UserCredentials.Email)
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if !shouldOverride {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
//override domain
|
||||
domainQuery := true
|
||||
if config.INFISICAL_URL_MANUAL_OVERRIDE != "" && config.INFISICAL_URL_MANUAL_OVERRIDE != util.INFISICAL_DEFAULT_API_URL {
|
||||
overrideDomain, err := DomainOverridePrompt()
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
//override domain
|
||||
domainQuery := true
|
||||
if config.INFISICAL_URL_MANUAL_OVERRIDE != "" && config.INFISICAL_URL_MANUAL_OVERRIDE != util.INFISICAL_DEFAULT_API_URL {
|
||||
overrideDomain, err := DomainOverridePrompt()
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
//if not override set INFISICAL_URL to exported var
|
||||
//set domainQuery to false
|
||||
if !overrideDomain {
|
||||
domainQuery = false
|
||||
config.INFISICAL_URL = config.INFISICAL_URL_MANUAL_OVERRIDE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//if not override set INFISICAL_URL to exported var
|
||||
//set domainQuery to false
|
||||
if !overrideDomain {
|
||||
domainQuery = false
|
||||
config.INFISICAL_URL = config.INFISICAL_URL_MANUAL_OVERRIDE
|
||||
//prompt user to select domain between Infisical cloud and self hosting
|
||||
if domainQuery {
|
||||
err = askForDomain()
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse domain url")
|
||||
}
|
||||
}
|
||||
var userCredentialsToBeStored models.UserCredentials
|
||||
|
||||
}
|
||||
|
||||
//prompt user to select domain between Infisical cloud and self hosting
|
||||
if domainQuery {
|
||||
err = askForDomain()
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse domain url")
|
||||
}
|
||||
}
|
||||
var userCredentialsToBeStored models.UserCredentials
|
||||
|
||||
interactiveLogin := false
|
||||
if cmd.Flags().Changed("interactive") {
|
||||
interactiveLogin = true
|
||||
cliDefaultLogin(&userCredentialsToBeStored)
|
||||
}
|
||||
|
||||
//call browser login function
|
||||
if !interactiveLogin {
|
||||
fmt.Println("Logging in via browser... To login via interactive mode run [infisical login -i]")
|
||||
userCredentialsToBeStored, err = browserCliLogin()
|
||||
if err != nil {
|
||||
//default to cli login on error
|
||||
interactiveLogin := false
|
||||
if cmd.Flags().Changed("interactive") {
|
||||
interactiveLogin = true
|
||||
cliDefaultLogin(&userCredentialsToBeStored)
|
||||
}
|
||||
|
||||
//call browser login function
|
||||
if !interactiveLogin {
|
||||
fmt.Println("Logging in via browser... To login via interactive mode run [infisical login -i]")
|
||||
userCredentialsToBeStored, err = browserCliLogin()
|
||||
if err != nil {
|
||||
//default to cli login on error
|
||||
cliDefaultLogin(&userCredentialsToBeStored)
|
||||
}
|
||||
}
|
||||
|
||||
err = util.StoreUserCredsInKeyRing(&userCredentialsToBeStored)
|
||||
if err != nil {
|
||||
log.Error().Msgf("Unable to store your credentials in system vault [%s]")
|
||||
log.Error().Msgf("\nTo trouble shoot further, read https://infisical.com/docs/cli/faq")
|
||||
log.Debug().Err(err)
|
||||
//return here
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
err = util.WriteInitalConfig(&userCredentialsToBeStored)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to write write to Infisical Config file. Please try again")
|
||||
}
|
||||
|
||||
// clear backed up secrets from prev account
|
||||
util.DeleteBackupSecrets()
|
||||
|
||||
whilte := color.New(color.FgGreen)
|
||||
boldWhite := whilte.Add(color.Bold)
|
||||
time.Sleep(time.Second * 1)
|
||||
boldWhite.Printf(">>>> Welcome to Infisical!")
|
||||
boldWhite.Printf(" You are now logged in as %v <<<< \n", userCredentialsToBeStored.Email)
|
||||
|
||||
plainBold := color.New(color.Bold)
|
||||
|
||||
plainBold.Println("\nQuick links")
|
||||
fmt.Println("- Learn to inject secrets into your application at https://infisical.com/docs/cli/usage")
|
||||
fmt.Println("- Stuck? Join our slack for quick support https://infisical.com/slack")
|
||||
Telemetry.CaptureEvent("cli-command:login", posthog.NewProperties().Set("infisical-backend", config.INFISICAL_URL).Set("version", util.CLI_VERSION))
|
||||
} else if loginMethod == "universal-auth" {
|
||||
|
||||
clientId, err := cmd.Flags().GetString("client-id")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
clientSecret, err := cmd.Flags().GetString("client-secret")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if clientId == "" {
|
||||
clientId = os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME)
|
||||
if clientId == "" {
|
||||
util.PrintErrorMessageAndExit("Please provide client-id")
|
||||
}
|
||||
}
|
||||
if clientSecret == "" {
|
||||
clientSecret = os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME)
|
||||
if clientSecret == "" {
|
||||
util.PrintErrorMessageAndExit("Please provide client-secret")
|
||||
}
|
||||
}
|
||||
|
||||
res, err := util.UniversalAuthLogin(clientId, clientSecret)
|
||||
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if plainOutput {
|
||||
fmt.Println(res.AccessToken)
|
||||
return
|
||||
}
|
||||
|
||||
boldGreen := color.New(color.FgGreen).Add(color.Bold)
|
||||
boldPlain := color.New(color.Bold)
|
||||
time.Sleep(time.Second * 1)
|
||||
boldGreen.Printf(">>>> Successfully authenticated with Universal Auth!\n\n")
|
||||
boldPlain.Printf("Universal Auth Access Token:\n%v", res.AccessToken)
|
||||
|
||||
plainBold := color.New(color.Bold)
|
||||
plainBold.Println("\n\nYou can use this access token to authenticate through other commands in the CLI.")
|
||||
|
||||
}
|
||||
|
||||
err = util.StoreUserCredsInKeyRing(&userCredentialsToBeStored)
|
||||
if err != nil {
|
||||
log.Error().Msgf("Unable to store your credentials in system vault [%s]")
|
||||
log.Error().Msgf("\nTo trouble shoot further, read https://infisical.com/docs/cli/faq")
|
||||
log.Debug().Err(err)
|
||||
//return here
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
err = util.WriteInitalConfig(&userCredentialsToBeStored)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to write write to Infisical Config file. Please try again")
|
||||
}
|
||||
|
||||
// clear backed up secrets from prev account
|
||||
util.DeleteBackupSecrets()
|
||||
|
||||
whilte := color.New(color.FgGreen)
|
||||
boldWhite := whilte.Add(color.Bold)
|
||||
time.Sleep(time.Second * 1)
|
||||
boldWhite.Printf(">>>> Welcome to Infisical!")
|
||||
boldWhite.Printf(" You are now logged in as %v <<<< \n", userCredentialsToBeStored.Email)
|
||||
|
||||
plainBold := color.New(color.Bold)
|
||||
|
||||
plainBold.Println("\nQuick links")
|
||||
fmt.Println("- Learn to inject secrets into your application at https://infisical.com/docs/cli/usage")
|
||||
fmt.Println("- Stuck? Join our slack for quick support https://infisical.com/slack")
|
||||
Telemetry.CaptureEvent("cli-command:login", posthog.NewProperties().Set("infisical-backend", config.INFISICAL_URL).Set("version", util.CLI_VERSION))
|
||||
},
|
||||
}
|
||||
|
||||
@ -313,6 +375,10 @@ func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) {
|
||||
func init() {
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
loginCmd.Flags().BoolP("interactive", "i", false, "login via the command line")
|
||||
loginCmd.Flags().String("method", "user", "login method [user, universal-auth]")
|
||||
loginCmd.Flags().String("client-id", "", "client id for universal auth")
|
||||
loginCmd.Flags().Bool("plain", false, "only output the token without any formatting")
|
||||
loginCmd.Flags().String("client-secret", "", "client secret for universal auth")
|
||||
}
|
||||
|
||||
func DomainOverridePrompt() (bool, error) {
|
||||
|
@ -40,8 +40,14 @@ func init() {
|
||||
rootCmd.PersistentFlags().StringP("log-level", "l", "info", "log level (trace, debug, info, warn, error, fatal)")
|
||||
rootCmd.PersistentFlags().Bool("telemetry", true, "Infisical collects non-sensitive telemetry data to enhance features and improve user experience. Participation is voluntary")
|
||||
rootCmd.PersistentFlags().StringVar(&config.INFISICAL_URL, "domain", util.INFISICAL_DEFAULT_API_URL, "Point the CLI to your own backend [can also set via environment variable name: INFISICAL_API_URL]")
|
||||
rootCmd.PersistentFlags().Bool("silent", false, "Disable output of tip/info messages. Useful when running in scripts or CI/CD pipelines.")
|
||||
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
if !util.IsRunningInDocker() {
|
||||
silent, err := cmd.Flags().GetBool("silent")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if !util.IsRunningInDocker() && !silent {
|
||||
util.CheckForUpdate()
|
||||
}
|
||||
}
|
||||
|
@ -62,8 +62,7 @@ var runCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
@ -73,6 +72,11 @@ var runCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
@ -103,7 +107,22 @@ var runCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports, Recursive: recursive}, projectConfigDir)
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(request, projectConfigDir)
|
||||
|
||||
if err != nil {
|
||||
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
@ -116,9 +135,16 @@ var runCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if shouldExpandSecrets {
|
||||
secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{
|
||||
InfisicalToken: infisicalToken,
|
||||
}, projectConfigDir)
|
||||
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, projectConfigDir)
|
||||
}
|
||||
|
||||
secretsByKey := getSecretsByKeys(secrets)
|
||||
@ -149,7 +175,15 @@ var runCmd = &cobra.Command{
|
||||
|
||||
log.Debug().Msgf("injecting the following environment variables into shell: %v", env)
|
||||
|
||||
Telemetry.CaptureEvent("cli-command:run", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("environment", environmentName).Set("isUsingServiceToken", infisicalToken != "").Set("single-command", strings.Join(args, " ")).Set("multi-command", cmd.Flag("command").Value.String()).Set("version", util.CLI_VERSION))
|
||||
Telemetry.CaptureEvent("cli-command:run",
|
||||
posthog.NewProperties().
|
||||
Set("secretsCount", len(secrets)).
|
||||
Set("environment", environmentName).
|
||||
Set("isUsingServiceToken", token.Type == util.SERVICE_TOKEN_IDENTIFIER).
|
||||
Set("isUsingUniversalAuthToken", token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER).
|
||||
Set("single-command", strings.Join(args, " ")).
|
||||
Set("multi-command", cmd.Flag("command").Value.String()).
|
||||
Set("version", util.CLI_VERSION))
|
||||
|
||||
if cmd.Flags().Changed("command") {
|
||||
command := cmd.Flag("command").Value.String()
|
||||
@ -204,6 +238,7 @@ func filterReservedEnvVars(env map[string]models.SingleEnvironmentVariable) {
|
||||
func init() {
|
||||
rootCmd.AddCommand(runCmd)
|
||||
runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
runCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from for machine identity")
|
||||
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
|
||||
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
runCmd.Flags().Bool("include-imports", true, "Import linked secrets ")
|
||||
|
@ -38,12 +38,12 @@ var secretsCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
@ -78,7 +78,22 @@ var secretsCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports, Recursive: recursive}, "")
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(request, "")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
@ -90,9 +105,15 @@ var secretsCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if shouldExpandSecrets {
|
||||
secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{
|
||||
InfisicalToken: infisicalToken,
|
||||
}, "")
|
||||
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
|
||||
visualize.PrintAllSecretDetails(secrets)
|
||||
@ -402,8 +423,12 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
shouldExpand, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
@ -413,6 +438,11 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secretsPath, err := cmd.Flags().GetString("path")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse path flag")
|
||||
@ -428,11 +458,37 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse path flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true, Recursive: recursive}, "")
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: true,
|
||||
Recursive: recursive,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(request, "")
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
|
||||
if shouldExpand {
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
|
||||
requestedSecrets := []models.SingleEnvironmentVariable{}
|
||||
|
||||
secretsMap := getSecretsByKeys(secrets)
|
||||
@ -475,8 +531,12 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
infisicalToken, err := util.GetInfisicalServiceToken(cmd)
|
||||
token, err := util.GetInfisicalToken(cmd)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
@ -486,7 +546,21 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true}, "")
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: true,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
request.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(request, "")
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
@ -686,19 +760,23 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
|
||||
|
||||
func init() {
|
||||
secretsGenerateExampleEnvCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsGenerateExampleEnvCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from for machine identity")
|
||||
secretsGenerateExampleEnvCmd.Flags().String("path", "/", "Fetch secrets from within a folder path")
|
||||
secretsCmd.AddCommand(secretsGenerateExampleEnvCmd)
|
||||
|
||||
secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsCmd.AddCommand(secretsGetCmd)
|
||||
secretsGetCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from for machine identity")
|
||||
secretsGetCmd.Flags().String("path", "/", "get secrets within a folder path")
|
||||
secretsGetCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
secretsGetCmd.Flags().Bool("raw-value", false, "Returns only the value of secret, only works with one secret")
|
||||
secretsGetCmd.Flags().Bool("recursive", false, "Fetch secrets from all sub-folders")
|
||||
secretsCmd.AddCommand(secretsGetCmd)
|
||||
|
||||
secretsCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||
secretsCmd.AddCommand(secretsSetCmd)
|
||||
secretsSetCmd.Flags().String("path", "/", "set secrets within a folder path")
|
||||
|
||||
// Only supports logged in users (JWT auth)
|
||||
secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
@ -707,6 +785,8 @@ func init() {
|
||||
secretsDeleteCmd.Flags().String("type", "personal", "the type of secret to delete: personal or shared (default: personal)")
|
||||
secretsDeleteCmd.Flags().String("path", "/", "get secrets within a folder path")
|
||||
secretsCmd.AddCommand(secretsDeleteCmd)
|
||||
|
||||
// Only supports logged in users (JWT auth)
|
||||
secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
@ -718,6 +798,7 @@ func init() {
|
||||
// Add getCmd, createCmd and deleteCmd flags here
|
||||
getCmd.Flags().StringP("path", "p", "/", "The path from where folders should be fetched from")
|
||||
getCmd.Flags().String("token", "", "Fetch folders using the infisical token")
|
||||
getCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from for machine identity")
|
||||
folderCmd.AddCommand(getCmd)
|
||||
|
||||
// Add createCmd flags here
|
||||
@ -735,6 +816,7 @@ func init() {
|
||||
// ** End of folders sub command
|
||||
|
||||
secretsCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from for machine identity")
|
||||
secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on")
|
||||
secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
secretsCmd.Flags().Bool("include-imports", true, "Imported linked secrets ")
|
||||
|
63
cli/packages/cmd/token.go
Normal file
63
cli/packages/cmd/token.go
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var tokenCmd = &cobra.Command{
|
||||
Use: "token",
|
||||
Short: "Manage your access tokens",
|
||||
DisableFlagsInUseLine: true,
|
||||
Example: "infisical token",
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
var tokenRenewCmd = &cobra.Command{
|
||||
Use: "renew [token]",
|
||||
Short: "Used to renew your universal auth access token",
|
||||
DisableFlagsInUseLine: true,
|
||||
Example: "infisical token renew <access-token>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// args[0] will be the <INSERT_TOKEN> from your command call
|
||||
token := args[0]
|
||||
|
||||
if strings.HasPrefix(token, "st.") {
|
||||
util.PrintErrorMessageAndExit("You are trying to renew a service token. You can only renew universal auth access tokens.")
|
||||
}
|
||||
|
||||
renewedAccessToken, err := util.RenewUniversalAuthAccessToken(token)
|
||||
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to renew token")
|
||||
}
|
||||
|
||||
boldGreen := color.New(color.FgGreen).Add(color.Bold)
|
||||
time.Sleep(time.Second * 1)
|
||||
boldGreen.Printf(">>>> Successfully renewed token!\n\n")
|
||||
boldGreen.Printf("Renewed Access Token:\n%v", renewedAccessToken)
|
||||
|
||||
plainBold := color.New(color.Bold)
|
||||
plainBold.Println("\n\nYou can use the new access token to authenticate through other commands in the CLI.")
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
tokenCmd.AddCommand(tokenRenewCmd)
|
||||
|
||||
rootCmd.AddCommand(tokenCmd)
|
||||
}
|
@ -59,6 +59,11 @@ type DynamicSecretLease struct {
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type TokenDetails struct {
|
||||
Type string
|
||||
Token string
|
||||
}
|
||||
|
||||
type SingleFolder struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
@ -97,10 +102,11 @@ type GetAllSecretsParameters struct {
|
||||
}
|
||||
|
||||
type GetAllFoldersParameters struct {
|
||||
WorkspaceId string
|
||||
Environment string
|
||||
FoldersPath string
|
||||
InfisicalToken string
|
||||
WorkspaceId string
|
||||
Environment string
|
||||
FoldersPath string
|
||||
InfisicalToken string
|
||||
UniversalAuthAccessToken string
|
||||
}
|
||||
|
||||
type CreateFolderParameters struct {
|
||||
@ -123,3 +129,8 @@ type ExpandSecretsAuthentication struct {
|
||||
InfisicalToken string
|
||||
UniversalAuthAccessToken string
|
||||
}
|
||||
|
||||
type MachineIdentityCredentials struct {
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
}
|
||||
|
@ -1,17 +1,23 @@
|
||||
package util
|
||||
|
||||
const (
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_DEFAULT_API_URL = "https://app.infisical.com/api"
|
||||
INFISICAL_DEFAULT_URL = "https://app.infisical.com"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
|
||||
SECRET_TYPE_PERSONAL = "personal"
|
||||
SECRET_TYPE_SHARED = "shared"
|
||||
KEYRING_SERVICE_NAME = "infisical"
|
||||
PERSONAL_SECRET_TYPE_NAME = "personal"
|
||||
SHARED_SECRET_TYPE_NAME = "shared"
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_DEFAULT_API_URL = "https://app.infisical.com/api"
|
||||
INFISICAL_DEFAULT_URL = "https://app.infisical.com"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
|
||||
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID"
|
||||
INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET"
|
||||
INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN"
|
||||
SECRET_TYPE_PERSONAL = "personal"
|
||||
SECRET_TYPE_SHARED = "shared"
|
||||
KEYRING_SERVICE_NAME = "infisical"
|
||||
PERSONAL_SECRET_TYPE_NAME = "personal"
|
||||
SHARED_SECRET_TYPE_NAME = "shared"
|
||||
|
||||
SERVICE_TOKEN_IDENTIFIER = "service-token"
|
||||
UNIVERSAL_AUTH_TOKEN_IDENTIFIER = "universal-auth-token"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -19,7 +19,7 @@ func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder
|
||||
|
||||
var foldersToReturn []models.SingleFolder
|
||||
var folderErr error
|
||||
if params.InfisicalToken == "" {
|
||||
if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" {
|
||||
|
||||
log.Debug().Msg("GetAllFolders: Trying to fetch folders using logged in details")
|
||||
|
||||
@ -44,11 +44,24 @@ func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder
|
||||
folders, err := GetFoldersViaJTW(loggedInUserDetails.UserCredentials.JTWToken, workspaceFile.WorkspaceId, params.Environment, params.FoldersPath)
|
||||
folderErr = err
|
||||
foldersToReturn = folders
|
||||
} else {
|
||||
} else if params.InfisicalToken != "" {
|
||||
log.Debug().Msg("GetAllFolders: Trying to fetch folders using service token")
|
||||
|
||||
// get folders via service token
|
||||
folders, err := GetFoldersViaServiceToken(params.InfisicalToken, params.WorkspaceId, params.Environment, params.FoldersPath)
|
||||
folderErr = err
|
||||
foldersToReturn = folders
|
||||
} else if params.UniversalAuthAccessToken != "" {
|
||||
log.Debug().Msg("GetAllFolders: Trying to fetch folders using universal auth")
|
||||
|
||||
if params.WorkspaceId == "" {
|
||||
PrintErrorMessageAndExit("Project ID is required when using machine identity")
|
||||
}
|
||||
|
||||
// get folders via machine identity
|
||||
folders, err := GetFoldersViaMachineIdentity(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.FoldersPath)
|
||||
folderErr = err
|
||||
foldersToReturn = folders
|
||||
}
|
||||
return foldersToReturn, folderErr
|
||||
}
|
||||
@ -132,6 +145,34 @@ func GetFoldersViaServiceToken(fullServiceToken string, workspaceId string, envi
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
func GetFoldersViaMachineIdentity(accessToken string, workspaceId string, envSlug string, foldersPath string) ([]models.SingleFolder, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
getFoldersRequest := api.GetFoldersV1Request{
|
||||
WorkspaceId: workspaceId,
|
||||
Environment: envSlug,
|
||||
FoldersPath: foldersPath,
|
||||
}
|
||||
|
||||
apiResponse, err := api.CallGetFoldersV1(httpClient, getFoldersRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var folders []models.SingleFolder
|
||||
|
||||
for _, folder := range apiResponse.Folders {
|
||||
folders = append(folders, models.SingleFolder{
|
||||
Name: folder.Name,
|
||||
ID: folder.ID,
|
||||
})
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
// CreateFolder creates a folder in Infisical
|
||||
func CreateFolder(params models.CreateFolderParameters) (models.SingleFolder, error) {
|
||||
loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||
|
@ -9,8 +9,11 @@ import (
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -64,18 +67,70 @@ func IsSecretTypeValid(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func GetInfisicalServiceToken(cmd *cobra.Command) (serviceToken string, err error) {
|
||||
func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err error) {
|
||||
infisicalToken, err := cmd.Flags().GetString("token")
|
||||
|
||||
if infisicalToken == "" {
|
||||
infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if infisicalToken == "" { // If no flag is passed, we first check for the universal auth access token env variable.
|
||||
infisicalToken = os.Getenv(INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME)
|
||||
|
||||
if infisicalToken == "" { // If it's still empty after the first env check, we check for the service token env variable.
|
||||
infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
if infisicalToken == "" { // If it's empty, we return nothing at all.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(infisicalToken, "st.") {
|
||||
return &models.TokenDetails{
|
||||
Type: SERVICE_TOKEN_IDENTIFIER,
|
||||
Token: infisicalToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &models.TokenDetails{
|
||||
Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER,
|
||||
Token: infisicalToken,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func UniversalAuthLogin(clientId string, clientSecret string) (api.UniversalAuthLoginResponse, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
|
||||
tokenResponse, err := api.CallUniversalAuthLogin(httpClient, api.UniversalAuthLoginRequest{ClientId: clientId, ClientSecret: clientSecret})
|
||||
if err != nil {
|
||||
return api.UniversalAuthLoginResponse{}, err
|
||||
}
|
||||
|
||||
return tokenResponse, nil
|
||||
}
|
||||
|
||||
func RenewUniversalAuthAccessToken(accessToken string) (string, error) {
|
||||
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
|
||||
request := api.UniversalAuthRefreshRequest{
|
||||
AccessToken: accessToken,
|
||||
}
|
||||
|
||||
tokenResponse, err := api.CallUniversalAuthRefreshAccessToken(httpClient, request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return infisicalToken, nil
|
||||
return tokenResponse.AccessToken, nil
|
||||
}
|
||||
|
||||
// Checks if the passed in email already exists in the users slice
|
||||
|
@ -159,7 +159,7 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
|
||||
httpClient.SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
getSecretsRequest := api.GetEncryptedSecretsV3Request{
|
||||
getSecretsRequest := api.GetRawSecretsV3Request{
|
||||
WorkspaceId: workspaceId,
|
||||
Environment: environmentName,
|
||||
IncludeImport: includeImports,
|
||||
@ -171,7 +171,8 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
|
||||
getSecretsRequest.SecretPath = secretsPath
|
||||
}
|
||||
|
||||
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{WorkspaceId: workspaceId, SecretPath: secretsPath, Environment: environmentName})
|
||||
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, getSecretsRequest)
|
||||
|
||||
if err != nil {
|
||||
return models.PlaintextSecretResult{}, err
|
||||
}
|
||||
@ -182,7 +183,7 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
|
||||
}
|
||||
|
||||
for _, secret := range rawSecrets.Secrets {
|
||||
plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, WorkspaceId: secret.Workspace})
|
||||
plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace})
|
||||
}
|
||||
|
||||
// if includeImports {
|
||||
@ -355,6 +356,11 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
||||
log.Debug().Msg("Trying to fetch secrets using service token")
|
||||
secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive)
|
||||
} else if params.UniversalAuthAccessToken != "" {
|
||||
|
||||
if params.WorkspaceId == "" {
|
||||
PrintErrorMessageAndExit("Project ID is required when using machine identity")
|
||||
}
|
||||
|
||||
log.Debug().Msg("Trying to fetch secrets using universal auth")
|
||||
res, err := GetPlainTextSecretsViaMachineIdentity(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive)
|
||||
|
||||
|
@ -12,4 +12,53 @@ The CLI uses authentication to verify your identity. When you enter the correct
|
||||
|
||||
To change where the login credentials are stored, visit the [vaults command](./vault).
|
||||
|
||||
If you have added multiple users, you can switch between the users by using the [user command](./user).
|
||||
If you have added multiple users, you can switch between the users by using the [user command](./user).
|
||||
|
||||
|
||||
### Flags
|
||||
<Accordion title="--method">
|
||||
```bash
|
||||
infisical login --method=<auth-method> # Optional, will default to 'user'.
|
||||
```
|
||||
|
||||
#### Valid values for the `method` flag are:
|
||||
- `user`: Login using email and password.
|
||||
- `universal-auth`: Login using a universal auth client ID and client secret.
|
||||
|
||||
<Info>
|
||||
When `method` is set to `universal-auth`, the `client-id` and `client-secret` flags are required. Optionally you can set the `INFISICAL_UNIVERSAL_AUTH_CLIENT_ID` and `INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET` environment variables instead of using the flags.
|
||||
|
||||
When you authenticate with universal auth, an access token will be printed to the console upon successful login. This token can be used to authenticate with the Infisical API and the CLI by passing it in the `--token` flag when applicable.
|
||||
|
||||
Use flag `--plain` along with `--silent` to print only the token in plain text when using the `universal-auth` method.
|
||||
|
||||
</Info>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="--client-id">
|
||||
```bash
|
||||
infisical login --client-id=<client-id> # Optional, required if --method=universal-auth.
|
||||
```
|
||||
|
||||
#### Description
|
||||
The client ID of the universal auth client. This is required if the `--method` flag is set to `universal-auth`.
|
||||
|
||||
<Tip>
|
||||
The `client-id` flag can be substituted with the `INFISICAL_UNIVERSAL_AUTH_CLIENT_ID` environment variable.
|
||||
</Tip>
|
||||
</Accordion>
|
||||
<Accordion title="--client-secret">
|
||||
```bash
|
||||
infisical login --client-secret=<client-secret> # Optional, required if --method=universal-auth.
|
||||
```
|
||||
#### Description
|
||||
The client secret of the universal auth client. This is required if the `--method` flag is set to `universal-auth`.
|
||||
|
||||
<Tip>
|
||||
The `client-secret` flag can be substituted with the `INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET` environment variable.
|
||||
</Tip>
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
|
21
docs/cli/commands/token.mdx
Normal file
21
docs/cli/commands/token.mdx
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "infisical token"
|
||||
description: "Manage your Infisical identity access tokens"
|
||||
---
|
||||
|
||||
```bash
|
||||
infisical service-token renew <ua-access-token>
|
||||
```
|
||||
|
||||
## Description
|
||||
The Infisical `token` command allows you to manage your universal auth access tokens.
|
||||
With this command, you can renew your access tokens. In the future more subcommands will be added to better help you manage your tokens through the CLI.
|
||||
|
||||
<Accordion title="token renew <access-token>" defaultOpen="true">
|
||||
Use this command to renew your access token. This command will renew your access token and output a renewed access token to the console.
|
||||
|
||||
```bash
|
||||
$ infisical token renew <ua-access-token>
|
||||
```
|
||||
|
||||
</Accordion>
|
@ -225,6 +225,7 @@
|
||||
"cli/commands/run",
|
||||
"cli/commands/secrets",
|
||||
"cli/commands/export",
|
||||
"cli/commands/token",
|
||||
"cli/commands/service-token",
|
||||
"cli/commands/vault",
|
||||
"cli/commands/user",
|
||||
@ -472,7 +473,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Secret tags",
|
||||
"group": "Secret Tags",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-tags/list",
|
||||
"api-reference/endpoints/secret-tags/create",
|
||||
@ -492,7 +493,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Secret imports",
|
||||
"group": "Secret Imports",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-imports/list",
|
||||
"api-reference/endpoints/secret-imports/create",
|
||||
|
@ -63,6 +63,30 @@
|
||||
border-color: #ebebeb;
|
||||
}
|
||||
|
||||
#content-area .mt-8 .rounded-xl{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .mt-8 .rounded-lg{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .mt-6 .rounded-xl{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .mt-6 .rounded-lg{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .mt-6 .rounded-md{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .mt-8 .rounded-md{
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area div.my-4{
|
||||
border-radius: 0;
|
||||
border-width: 1px;
|
||||
@ -78,6 +102,10 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area a {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#content-area .not-prose {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => {
|
||||
const [searchRoles, setSearchRoles] = useState("");
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const);
|
||||
|
||||
const { data: roles, isLoading: isRolesLoading } = useGetOrgRoles(orgId);
|
||||
@ -49,7 +49,7 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => {
|
||||
handlePopUpClose("deleteRole");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createNotification({ type: "error", text: "Failed to create role" });
|
||||
createNotification({ type: "error", text: "Failed to delete role" });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@ type Props = {
|
||||
|
||||
export const ProjectRoleList = ({ onSelectRole }: Props) => {
|
||||
const [searchRoles, setSearchRoles] = useState("");
|
||||
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const);
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
@ -50,7 +50,7 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => {
|
||||
handlePopUpClose("deleteRole");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createNotification({ type: "error", text: "Failed to create role" });
|
||||
createNotification({ type: "error", text: "Failed to delete role" });
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user