Compare commits

...

14 Commits

Author SHA1 Message Date
6f2043dc26 Merge pull request #3714 from akhilmhdh/feat/sort-access-control
feat: added back the describeReadValue permission to default roles
2025-06-03 23:55:19 +05:30
=
95fcf560a5 feat: added back the describeReadValue permission to default roles 2025-06-03 23:46:59 +05:30
274952544f Merge pull request #3711 from akhilmhdh/feat/sort-access-control
feat: added sort for roles in both user and identity details view
2025-06-03 12:59:21 -04:00
d23beaedf1 Merge pull request #3707 from Infisical/misc/workspace-file-now-only-needed-when-project-id-omitted
misc: workspace file now only needed when project id is omitted (CLI)
2025-06-04 00:24:40 +08:00
=
817e762e6b feat: added sort for roles in both user and identity details view 2025-06-03 21:04:02 +05:30
440c45fd42 Merge pull request #3695 from Infisical/daniel/identity-get-projects
fix: allow identities to list projects they are apart of
2025-06-03 16:52:03 +04:00
893a042c25 Merge pull request #3698 from Infisical/daniel/cli-api-errors
fix(cli): improve error handling
2025-06-03 16:49:37 +04:00
f3fb65fcc3 misc: update error message being displayed 2025-06-03 20:06:42 +08:00
c0add863be misc: workspace file now only needed when project id is omitted (CLI) 2025-06-03 19:41:37 +08:00
22f32e060b filter out random request ID value 2025-06-01 21:31:26 +04:00
b4f26aac25 fix: tests failing 2025-06-01 21:26:16 +04:00
b634a6c371 requested changes 2025-06-01 21:10:05 +04:00
080ae5ce6f fix(cli): improve error handling 2025-06-01 20:22:15 +04:00
dfcf613023 fix: allow identities to list projects they are apart of 2025-06-01 00:12:56 +04:00
15 changed files with 297 additions and 112 deletions

View File

@ -148,6 +148,7 @@ const buildAdminPermissionRules = () => {
can(
[
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Edit,
@ -228,6 +229,7 @@ const buildMemberPermissionRules = () => {
can(
[
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Create,

View File

@ -173,7 +173,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const workspaces = await server.services.project.getProjects({
includeRoles: req.query.includeRoles,

View File

@ -412,7 +412,15 @@ export const identityProjectDALFactory = (db: TDbClient) => {
}
]
});
return members;
return members.map((el) => ({
...el,
roles: el.roles.sort((a, b) => {
const roleA = (a.customRoleName || a.role).toLowerCase();
const roleB = (b.customRoleName || b.role).toLowerCase();
return roleA.localeCompare(roleB);
})
}));
} catch (error) {
throw new DatabaseError({ error, name: "FindByProjectId" });
}

View File

@ -92,7 +92,8 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project)
)
.where({ isGhost: false });
.where({ isGhost: false })
.orderBy(`${TableName.Users}.username` as "username");
const members = sqlNestRelationships({
data: docs,
@ -149,7 +150,14 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
}
]
});
return members;
return members.map((el) => ({
...el,
roles: el.roles.sort((a, b) => {
const roleA = (a.customRoleName || a.role).toLowerCase();
const roleB = (b.customRoleName || b.role).toLowerCase();
return roleA.localeCompare(roleB);
})
}));
} catch (error) {
throw new DatabaseError({ error, name: "Find all project members" });
}

View File

@ -22,6 +22,56 @@ export type TProjectDALFactory = ReturnType<typeof projectDALFactory>;
export const projectDALFactory = (db: TDbClient) => {
const projectOrm = ormify(db, TableName.Project);
const findIdentityProjects = async (identityId: string, orgId: string, projectType: ProjectType | "all") => {
try {
const workspaces = await db(TableName.IdentityProjectMembership)
.where({ identityId })
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
if (projectType !== "all") {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName")
)
.orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);
const nestedWorkspaces = sqlNestRelationships({
data: workspaces,
key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }),
childrenMapper: [
{
key: "envId",
label: "environments" as const,
mapper: ({ envId: id, envSlug: slug, envName: name }) => ({
id,
slug,
name
})
}
]
});
return nestedWorkspaces.map((workspace) => ({
...workspace,
organization: workspace.orgId
}));
} catch (error) {
throw new DatabaseError({ error, name: "Find identity projects" });
}
};
const findUserProjects = async (userId: string, orgId: string, projectType: ProjectType | "all") => {
try {
const workspaces = await db
@ -443,6 +493,7 @@ export const projectDALFactory = (db: TDbClient) => {
return {
...projectOrm,
findUserProjects,
findIdentityProjects,
setProjectUpgradeStatus,
findAllProjectsByIdentity,
findProjectGhostUser,

View File

@ -573,12 +573,16 @@ export const projectServiceFactory = ({
const getProjects = async ({
actorId,
actor,
includeRoles,
actorAuthMethod,
actorOrgId,
type = ProjectType.SecretManager
}: TListProjectsDTO) => {
const workspaces = await projectDAL.findUserProjects(actorId, actorOrgId, type);
const workspaces =
actor === ActorType.IDENTITY
? await projectDAL.findIdentityProjects(actorId, actorOrgId, type)
: await projectDAL.findUserProjects(actorId, actorOrgId, type);
if (includeRoles) {
const { permission } = await permissionService.getUserOrgPermission(

View File

@ -12,6 +12,35 @@ import (
const USER_AGENT = "cli"
const (
operationCallGetRawSecretsV3 = "CallGetRawSecretsV3"
operationCallGetEncryptedWorkspaceKey = "CallGetEncryptedWorkspaceKey"
operationCallGetServiceTokenDetails = "CallGetServiceTokenDetails"
operationCallLogin1V3 = "CallLogin1V3"
operationCallVerifyMfaToken = "CallVerifyMfaToken"
operationCallLogin2V3 = "CallLogin2V3"
operationCallGetAllOrganizations = "CallGetAllOrganizations"
operationCallSelectOrganization = "CallSelectOrganization"
operationCallGetAllWorkSpacesUserBelongsTo = "CallGetAllWorkSpacesUserBelongsTo"
operationCallGetProjectById = "CallGetProjectById"
operationCallIsAuthenticated = "CallIsAuthenticated"
operationCallGetNewAccessTokenWithRefreshToken = "CallGetNewAccessTokenWithRefreshToken"
operationCallGetFoldersV1 = "CallGetFoldersV1"
operationCallCreateFolderV1 = "CallCreateFolderV1"
operationCallDeleteFolderV1 = "CallDeleteFolderV1"
operationCallDeleteSecretsV3 = "CallDeleteSecretsV3"
operationCallCreateServiceToken = "CallCreateServiceToken"
operationCallUniversalAuthLogin = "CallUniversalAuthLogin"
operationCallMachineIdentityRefreshAccessToken = "CallMachineIdentityRefreshAccessToken"
operationCallFetchSingleSecretByName = "CallFetchSingleSecretByName"
operationCallCreateRawSecretsV3 = "CallCreateRawSecretsV3"
operationCallUpdateRawSecretsV3 = "CallUpdateRawSecretsV3"
operationCallRegisterGatewayIdentityV1 = "CallRegisterGatewayIdentityV1"
operationCallExchangeRelayCertV1 = "CallExchangeRelayCertV1"
operationCallGatewayHeartBeatV1 = "CallGatewayHeartBeatV1"
operationCallBootstrapInstance = "CallBootstrapInstance"
)
func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) {
endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId)
var result GetEncryptedWorkspaceKeyResponse
@ -22,11 +51,11 @@ func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncrypted
Get(endpoint)
if err != nil {
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err)
return GetEncryptedWorkspaceKeyResponse{}, NewGenericRequestError(operationCallGetEncryptedWorkspaceKey, err)
}
if response.IsError() {
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response [%v %v] [status-code=%v]", response.Request.Method, response.Request.URL, response.StatusCode())
return GetEncryptedWorkspaceKeyResponse{}, NewAPIErrorWithResponse(operationCallGetEncryptedWorkspaceKey, response, nil)
}
return result, nil
@ -41,11 +70,11 @@ func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDeta
Get(fmt.Sprintf("%v/v2/service-token", config.INFISICAL_URL))
if err != nil {
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err)
return GetServiceTokenDetailsResponse{}, NewGenericRequestError(operationCallGetServiceTokenDetails, err)
}
if response.IsError() {
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response)
return GetServiceTokenDetailsResponse{}, NewAPIErrorWithResponse(operationCallGetServiceTokenDetails, response, nil)
}
return tokenDetailsResponse, nil
@ -61,11 +90,11 @@ func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLo
Post(fmt.Sprintf("%v/v3/auth/login1", config.INFISICAL_URL))
if err != nil {
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V3: Unable to complete api request [err=%s]", err)
return GetLoginOneV2Response{}, NewGenericRequestError(operationCallLogin1V3, err)
}
if response.IsError() {
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V3: Unsuccessful response: [response=%s]", response)
return GetLoginOneV2Response{}, NewAPIErrorWithResponse(operationCallLogin1V3, response, nil)
}
return loginOneV2Response, nil
@ -99,7 +128,7 @@ func CallVerifyMfaToken(httpClient *resty.Client, request VerifyMfaTokenRequest)
}
if err != nil {
return nil, nil, fmt.Errorf("CallVerifyMfaToken: Unable to complete api request [err=%s]", err)
return nil, nil, NewGenericRequestError(operationCallVerifyMfaToken, err)
}
if response.IsError() {
@ -135,11 +164,11 @@ func CallLogin2V2(httpClient *resty.Client, request GetLoginTwoV2Request) (GetLo
}
if err != nil {
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V3: Unable to complete api request [err=%s]", err)
return GetLoginTwoV2Response{}, NewGenericRequestError(operationCallLogin2V3, err)
}
if response.IsError() {
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V3: Unsuccessful response: [response=%s]", response)
return GetLoginTwoV2Response{}, NewAPIErrorWithResponse(operationCallLogin2V3, response, nil)
}
return loginTwoV2Response, nil
@ -154,11 +183,11 @@ func CallGetAllOrganizations(httpClient *resty.Client) (GetOrganizationsResponse
Get(fmt.Sprintf("%v/v1/organization", config.INFISICAL_URL))
if err != nil {
return GetOrganizationsResponse{}, err
return GetOrganizationsResponse{}, NewGenericRequestError(operationCallGetAllOrganizations, err)
}
if response.IsError() {
return GetOrganizationsResponse{}, fmt.Errorf("CallGetAllOrganizations: Unsuccessful response: [response=%v]", response)
return GetOrganizationsResponse{}, NewAPIErrorWithResponse(operationCallGetAllOrganizations, response, nil)
}
return orgResponse, nil
@ -175,11 +204,11 @@ func CallSelectOrganization(httpClient *resty.Client, request SelectOrganization
Post(fmt.Sprintf("%v/v3/auth/select-organization", config.INFISICAL_URL))
if err != nil {
return SelectOrganizationResponse{}, err
return SelectOrganizationResponse{}, NewGenericRequestError(operationCallSelectOrganization, err)
}
if response.IsError() {
return SelectOrganizationResponse{}, fmt.Errorf("CallSelectOrganization: Unsuccessful response: [response=%v]", response)
return SelectOrganizationResponse{}, NewAPIErrorWithResponse(operationCallSelectOrganization, response, nil)
}
return selectOrgResponse, nil
@ -214,11 +243,11 @@ func CallGetProjectById(httpClient *resty.Client, id string) (Project, error) {
Get(fmt.Sprintf("%v/v1/workspace/%s", config.INFISICAL_URL, id))
if err != nil {
return Project{}, err
return Project{}, NewGenericRequestError(operationCallGetProjectById, err)
}
if response.IsError() {
return Project{}, fmt.Errorf("CallGetProjectById: Unsuccessful response: [response=%v]", response)
return Project{}, NewAPIErrorWithResponse(operationCallGetProjectById, response, nil)
}
return projectResponse.Project, nil
@ -237,7 +266,7 @@ func CallIsAuthenticated(httpClient *resty.Client) bool {
}
if response.IsError() {
log.Debug().Msgf("CallIsAuthenticated: Unsuccessful response: [response=%v]", response)
log.Debug().Msgf("%s: Unsuccessful response: [response=%v]", operationCallIsAuthenticated, response)
return false
}
@ -257,11 +286,11 @@ func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToke
Post(fmt.Sprintf("%v/v1/auth/token", config.INFISICAL_URL))
if err != nil {
return GetNewAccessTokenWithRefreshTokenResponse{}, err
return GetNewAccessTokenWithRefreshTokenResponse{}, NewGenericRequestError(operationCallGetNewAccessTokenWithRefreshToken, err)
}
if response.IsError() {
return GetNewAccessTokenWithRefreshTokenResponse{}, fmt.Errorf("CallGetNewAccessTokenWithRefreshToken: Unsuccessful response: [response=%v]", response)
return GetNewAccessTokenWithRefreshTokenResponse{}, NewAPIErrorWithResponse(operationCallGetNewAccessTokenWithRefreshToken, response, nil)
}
return newAccessToken, nil
@ -280,11 +309,11 @@ func CallGetFoldersV1(httpClient *resty.Client, request GetFoldersV1Request) (Ge
response, err := httpRequest.Get(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
if err != nil {
return GetFoldersV1Response{}, fmt.Errorf("CallGetFoldersV1: Unable to complete api request [err=%v]", err)
return GetFoldersV1Response{}, NewGenericRequestError(operationCallGetFoldersV1, err)
}
if response.IsError() {
return GetFoldersV1Response{}, fmt.Errorf("CallGetFoldersV1: Unsuccessful [response=%s]", response)
return GetFoldersV1Response{}, NewAPIErrorWithResponse(operationCallGetFoldersV1, response, nil)
}
return foldersResponse, nil
@ -300,11 +329,11 @@ func CallCreateFolderV1(httpClient *resty.Client, request CreateFolderV1Request)
response, err := httpRequest.Post(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
if err != nil {
return CreateFolderV1Response{}, fmt.Errorf("CallCreateFolderV1: Unable to complete api request [err=%s]", err)
return CreateFolderV1Response{}, NewGenericRequestError(operationCallCreateFolderV1, err)
}
if response.IsError() {
return CreateFolderV1Response{}, fmt.Errorf("CallCreateFolderV1: Unsuccessful [response=%s]", response.String())
return CreateFolderV1Response{}, NewAPIErrorWithResponse(operationCallCreateFolderV1, response, nil)
}
return folderResponse, nil
@ -321,11 +350,11 @@ func CallDeleteFolderV1(httpClient *resty.Client, request DeleteFolderV1Request)
response, err := httpRequest.Delete(fmt.Sprintf("%v/v1/folders/%v", config.INFISICAL_URL, request.FolderName))
if err != nil {
return DeleteFolderV1Response{}, fmt.Errorf("CallDeleteFolderV1: Unable to complete api request [err=%s]", err)
return DeleteFolderV1Response{}, NewGenericRequestError(operationCallDeleteFolderV1, err)
}
if response.IsError() {
return DeleteFolderV1Response{}, fmt.Errorf("CallDeleteFolderV1: Unsuccessful [response=%s]", response.String())
return DeleteFolderV1Response{}, NewAPIErrorWithResponse(operationCallDeleteFolderV1, response, nil)
}
return folderResponse, nil
@ -342,11 +371,12 @@ func CallDeleteSecretsRawV3(httpClient *resty.Client, request DeleteSecretV3Requ
Delete(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
if err != nil {
return fmt.Errorf("CallDeleteSecretsV3: Unable to complete api request [err=%s]", err)
return NewGenericRequestError(operationCallDeleteSecretsV3, err)
}
if response.IsError() {
return fmt.Errorf("CallDeleteSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response)
additionalContext := "Please make sure your secret path, workspace and environment name are all correct."
return NewAPIErrorWithResponse(operationCallDeleteSecretsV3, response, &additionalContext)
}
return nil
@ -362,11 +392,11 @@ func CallCreateServiceToken(httpClient *resty.Client, request CreateServiceToken
Post(fmt.Sprintf("%v/v2/service-token/", config.INFISICAL_URL))
if err != nil {
return CreateServiceTokenResponse{}, fmt.Errorf("CallCreateServiceToken: Unable to complete api request [err=%s]", err)
return CreateServiceTokenResponse{}, NewGenericRequestError(operationCallCreateServiceToken, err)
}
if response.IsError() {
return CreateServiceTokenResponse{}, fmt.Errorf("CallCreateServiceToken: Unsuccessful response [%v %v] [status-code=%v]", response.Request.Method, response.Request.URL, response.StatusCode())
return CreateServiceTokenResponse{}, NewAPIErrorWithResponse(operationCallCreateServiceToken, response, nil)
}
return createServiceTokenResponse, nil
@ -382,11 +412,11 @@ func CallUniversalAuthLogin(httpClient *resty.Client, request UniversalAuthLogin
Post(fmt.Sprintf("%v/v1/auth/universal-auth/login/", config.INFISICAL_URL))
if err != nil {
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unable to complete api request [err=%s]", err)
return UniversalAuthLoginResponse{}, NewGenericRequestError(operationCallUniversalAuthLogin, err)
}
if response.IsError() {
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return UniversalAuthLoginResponse{}, NewAPIErrorWithResponse(operationCallUniversalAuthLogin, response, nil)
}
return universalAuthLoginResponse, nil
@ -402,11 +432,11 @@ func CallMachineIdentityRefreshAccessToken(httpClient *resty.Client, request Uni
Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL))
if err != nil {
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unable to complete api request [err=%s]", err)
return UniversalAuthRefreshResponse{}, NewGenericRequestError(operationCallMachineIdentityRefreshAccessToken, err)
}
if response.IsError() {
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return UniversalAuthRefreshResponse{}, NewAPIErrorWithResponse(operationCallMachineIdentityRefreshAccessToken, response, nil)
}
return universalAuthRefreshResponse, nil
@ -441,19 +471,19 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL))
if err != nil {
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unable to complete api request [err=%w]", err)
return GetRawSecretsV3Response{}, NewGenericRequestError(operationCallGetRawSecretsV3, err)
}
if response.IsError() &&
(strings.Contains(response.String(), "bot_not_found_error") ||
strings.Contains(strings.ToLower(response.String()), "failed to find bot key") ||
strings.Contains(strings.ToLower(response.String()), "bot is not active")) {
return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self-hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release.
`, request.WorkspaceId)
additionalContext := fmt.Sprintf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self-hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release.`, request.WorkspaceId)
return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, &additionalContext)
}
if response.IsError() {
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, nil)
}
getRawSecretsV3Response.ETag = response.Header().Get(("etag"))
@ -477,11 +507,11 @@ func CallFetchSingleSecretByName(httpClient *resty.Client, request GetRawSecretV
Get(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
if err != nil {
return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unable to complete api request [err=%w]", err)
return GetRawSecretV3ByNameResponse{}, NewGenericRequestError(operationCallFetchSingleSecretByName, err)
}
if response.IsError() {
return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return GetRawSecretV3ByNameResponse{}, NewAPIErrorWithResponse(operationCallFetchSingleSecretByName, response, nil)
}
getRawSecretV3ByNameResponse.ETag = response.Header().Get(("etag"))
@ -517,11 +547,11 @@ func CallCreateRawSecretsV3(httpClient *resty.Client, request CreateRawSecretV3R
Post(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
if err != nil {
return fmt.Errorf("CallCreateRawSecretsV3: Unable to complete api request [err=%w]", err)
return NewGenericRequestError(operationCallCreateRawSecretsV3, err)
}
if response.IsError() {
return fmt.Errorf("CallCreateRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return NewAPIErrorWithResponse(operationCallCreateRawSecretsV3, response, nil)
}
return nil
@ -535,11 +565,11 @@ func CallUpdateRawSecretsV3(httpClient *resty.Client, request UpdateRawSecretByN
Patch(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
if err != nil {
return fmt.Errorf("CallUpdateRawSecretsV3: Unable to complete api request [err=%w]", err)
return NewGenericRequestError(operationCallUpdateRawSecretsV3, err)
}
if response.IsError() {
return fmt.Errorf("CallUpdateRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return NewAPIErrorWithResponse(operationCallUpdateRawSecretsV3, response, nil)
}
return nil
@ -554,11 +584,11 @@ func CallRegisterGatewayIdentityV1(httpClient *resty.Client) (*GetRelayCredentia
Post(fmt.Sprintf("%v/v1/gateways/register-identity", config.INFISICAL_URL))
if err != nil {
return nil, fmt.Errorf("CallRegisterGatewayIdentityV1: Unable to complete api request [err=%w]", err)
return nil, NewGenericRequestError(operationCallRegisterGatewayIdentityV1, err)
}
if response.IsError() {
return nil, fmt.Errorf("CallRegisterGatewayIdentityV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return nil, NewAPIErrorWithResponse(operationCallRegisterGatewayIdentityV1, response, nil)
}
return &resBody, nil
@ -574,11 +604,11 @@ func CallExchangeRelayCertV1(httpClient *resty.Client, request ExchangeRelayCert
Post(fmt.Sprintf("%v/v1/gateways/exchange-cert", config.INFISICAL_URL))
if err != nil {
return nil, fmt.Errorf("CallExchangeRelayCertV1: Unable to complete api request [err=%w]", err)
return nil, NewGenericRequestError(operationCallExchangeRelayCertV1, err)
}
if response.IsError() {
return nil, fmt.Errorf("CallExchangeRelayCertV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return nil, NewAPIErrorWithResponse(operationCallExchangeRelayCertV1, response, nil)
}
return &resBody, nil
@ -591,11 +621,11 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
Post(fmt.Sprintf("%v/v1/gateways/heartbeat", config.INFISICAL_URL))
if err != nil {
return fmt.Errorf("CallGatewayHeartBeatV1: Unable to complete api request [err=%w]", err)
return NewGenericRequestError(operationCallGatewayHeartBeatV1, err)
}
if response.IsError() {
return fmt.Errorf("CallGatewayHeartBeatV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return NewAPIErrorWithResponse(operationCallGatewayHeartBeatV1, response, nil)
}
return nil
@ -611,11 +641,11 @@ func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRe
Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain))
if err != nil {
return nil, fmt.Errorf("CallBootstrapInstance: Unable to complete api request [err=%w]", err)
return nil, NewGenericRequestError(operationCallBootstrapInstance, err)
}
if response.IsError() {
return nil, fmt.Errorf("CallBootstrapInstance: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
return nil, NewAPIErrorWithResponse(operationCallBootstrapInstance, response, nil)
}
return resBody, nil

View File

@ -0,0 +1,80 @@
package api
import (
"fmt"
"github.com/go-resty/resty/v2"
"github.com/infisical/go-sdk/packages/util"
)
type GenericRequestError struct {
err error
operation string
}
func (e *GenericRequestError) Error() string {
return fmt.Sprintf("%s: Unable to complete api request [err=%v]", e.operation, e.err)
}
func NewGenericRequestError(operation string, err error) *GenericRequestError {
return &GenericRequestError{err: err, operation: operation}
}
// APIError represents an error response from the API
type APIError struct {
AdditionalContext string `json:"additionalContext,omitempty"`
Operation string `json:"operation"`
Method string `json:"method"`
URL string `json:"url"`
StatusCode int `json:"statusCode"`
ErrorMessage string `json:"message,omitempty"`
ReqId string `json:"reqId,omitempty"`
}
func (e *APIError) Error() string {
msg := fmt.Sprintf(
"%s Unsuccessful response [%v %v] [status-code=%v] [request-id=%v]",
e.Operation,
e.Method,
e.URL,
e.StatusCode,
e.ReqId,
)
if e.ErrorMessage != "" {
msg = fmt.Sprintf("%s [message=\"%s\"]", msg, e.ErrorMessage)
}
if e.AdditionalContext != "" {
msg = fmt.Sprintf("%s [additional-context=\"%s\"]", msg, e.AdditionalContext)
}
return msg
}
func NewAPIErrorWithResponse(operation string, res *resty.Response, additionalContext *string) error {
errorMessage := util.TryParseErrorBody(res)
reqId := util.TryExtractReqId(res)
if res == nil {
return NewGenericRequestError(operation, fmt.Errorf("response is nil"))
}
apiError := &APIError{
Operation: operation,
Method: res.Request.Method,
URL: res.Request.URL,
StatusCode: res.StatusCode(),
ReqId: reqId,
}
if additionalContext != nil && *additionalContext != "" {
apiError.AdditionalContext = *additionalContext
}
if errorMessage != "" {
apiError.ErrorMessage = errorMessage
}
return apiError
}

View File

@ -63,7 +63,7 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) {
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -72,7 +72,6 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) {
infisicalToken = token.Token
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {
@ -181,7 +180,7 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -190,7 +189,6 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
infisicalToken = token.Token
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {
@ -312,7 +310,7 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -321,7 +319,6 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
infisicalToken = token.Token
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {
@ -420,7 +417,7 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -429,7 +426,6 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
infisicalToken = token.Token
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {
@ -527,7 +523,7 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -536,7 +532,6 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
infisicalToken = token.Token
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {

View File

@ -112,7 +112,7 @@ var createCmd = &cobra.Command{
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get workspace file")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
@ -180,7 +180,7 @@ var deleteCmd = &cobra.Command{
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get workspace file")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId

View File

@ -158,10 +158,6 @@ var secretsSetCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
if token == nil {
util.RequireLocalWorkspaceFile()
}
environmentName, _ := cmd.Flags().GetString("env")
if !cmd.Flags().Changed("env") {
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
@ -175,6 +171,13 @@ var secretsSetCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag")
}
if token == nil && projectId == "" {
_, err := util.GetWorkSpaceFromFile()
if err != nil {
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
}
secretsPath, err := cmd.Flags().GetString("path")
if err != nil {
util.HandleError(err, "Unable to parse flag")
@ -225,7 +228,7 @@ var secretsSetCmd = &cobra.Command{
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "unable to get your local config details [err=%v]")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
@ -308,7 +311,7 @@ var secretsDeleteCmd = &cobra.Command{
if projectId == "" {
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
projectId = workspaceFile.WorkspaceId
}
@ -317,7 +320,6 @@ var secretsDeleteCmd = &cobra.Command{
httpClient.SetAuthToken(token.Token)
} else {
util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil {

View File

@ -15,7 +15,6 @@ func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder
var folderErr error
if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" {
RequireLogin()
RequireLocalWorkspaceFile()
log.Debug().Msg("GetAllFolders: Trying to fetch folders using logged in details")
@ -28,16 +27,15 @@ func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder
loggedInUserDetails = EstablishUserLoginSession()
}
workspaceFile, err := GetWorkSpaceFromFile()
if err != nil {
return nil, err
if params.WorkspaceId == "" {
workspaceFile, err := GetWorkSpaceFromFile()
if err != nil {
PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
params.WorkspaceId = workspaceFile.WorkspaceId
}
if params.WorkspaceId != "" {
workspaceFile.WorkspaceId = params.WorkspaceId
}
folders, err := GetFoldersViaJTW(loggedInUserDetails.UserCredentials.JTWToken, workspaceFile.WorkspaceId, params.Environment, params.FoldersPath)
folders, err := GetFoldersViaJTW(loggedInUserDetails.UserCredentials.JTWToken, params.WorkspaceId, params.Environment, params.FoldersPath)
folderErr = err
foldersToReturn = folders
} else if params.InfisicalToken != "" {
@ -186,7 +184,6 @@ func CreateFolder(params models.CreateFolderParameters) (models.SingleFolder, er
// If no token is provided, we will try to get the token from the current logged in user
if params.InfisicalToken == "" {
RequireLogin()
RequireLocalWorkspaceFile()
loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true)
if err != nil {
@ -235,7 +232,6 @@ func DeleteFolder(params models.DeleteFolderParameters) ([]models.SingleFolder,
// If no token is provided, we will try to get the token from the current logged in user
if params.InfisicalToken == "" {
RequireLogin()
RequireLocalWorkspaceFile()
loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true)

View File

@ -251,10 +251,15 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
var errorToReturn error
if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" {
if projectConfigFilePath == "" {
RequireLocalWorkspaceFile()
} else {
ValidateWorkspaceFile(projectConfigFilePath)
if params.WorkspaceId == "" {
if projectConfigFilePath == "" {
_, err := GetWorkSpaceFromFile()
if err != nil {
PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
} else {
ValidateWorkspaceFile(projectConfigFilePath)
}
}
RequireLogin()
@ -276,29 +281,28 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
loggedInUserDetails = EstablishUserLoginSession()
}
var infisicalDotJson models.WorkspaceConfigFile
if params.WorkspaceId == "" {
var infisicalDotJson models.WorkspaceConfigFile
if projectConfigFilePath == "" {
projectConfig, err := GetWorkSpaceFromFile()
if err != nil {
return nil, err
if projectConfigFilePath == "" {
projectConfig, err := GetWorkSpaceFromFile()
if err != nil {
PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
infisicalDotJson = projectConfig
} else {
projectConfig, err := GetWorkSpaceFromFilePath(projectConfigFilePath)
if err != nil {
return nil, err
}
infisicalDotJson = projectConfig
}
infisicalDotJson = projectConfig
} else {
projectConfig, err := GetWorkSpaceFromFilePath(projectConfigFilePath)
if err != nil {
return nil, err
}
infisicalDotJson = projectConfig
params.WorkspaceId = infisicalDotJson.WorkspaceId
}
if params.WorkspaceId != "" {
infisicalDotJson.WorkspaceId = params.WorkspaceId
}
res, err := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, infisicalDotJson.WorkspaceId,
res, err := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, params.WorkspaceId,
params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, true)
log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", err)
@ -307,7 +311,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
if err != nil {
return nil, err
}
WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey, res.Secrets)
WriteBackupSecrets(params.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey, res.Secrets)
}
secretsToReturn = res.Secrets
@ -316,7 +320,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
if !isConnected {
backupEncryptionKey, _ := GetBackupEncryptionKey()
if backupEncryptionKey != nil {
backedUpSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey)
backedUpSecrets, err := ReadBackupSecrets(params.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey)
if len(backedUpSecrets) > 0 {
PrintWarning("Unable to fetch the latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug")
secretsToReturn = backedUpSecrets

View File

@ -1,4 +1,4 @@
error: CallGetRawSecretsV3: Unsuccessful response [GET https://app.infisical.com/api/v3/secrets/raw?environment=invalid-env&expandSecretReferences=true&include_imports=true&recursive=true&secretPath=%2F&workspaceId=bef697d4-849b-4a75-b284-0922f87f8ba2] [status-code=404] [response={"error":"NotFound","message":"Environment with slug 'invalid-env' in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2 not found","statusCode":404}]
error: CallGetRawSecretsV3 Unsuccessful response [GET https://app.infisical.com/api/v3/secrets/raw?environment=invalid-env&expandSecretReferences=true&include_imports=true&recursive=true&secretPath=%2F&workspaceId=bef697d4-849b-4a75-b284-0922f87f8ba2] [status-code=404] [request-id=<unknown-value>] [message="Environment with slug 'invalid-env' in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2 not found"]
If this issue continues, get support at https://infisical.com/slack

View File

@ -6,6 +6,7 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strings"
)
@ -71,7 +72,11 @@ func SetupCli() {
}
func FilterRequestID(input string) string {
// Find the JSON part of the error message
requestIDPattern := regexp.MustCompile(`\[request-id=[^\]]+\]`)
reqIDPattern := regexp.MustCompile(`\[reqId=[^\]]+\]`)
input = requestIDPattern.ReplaceAllString(input, "[request-id=<unknown-value>]")
input = reqIDPattern.ReplaceAllString(input, "[reqId=<unknown-value>]")
start := strings.Index(input, "{")
end := strings.LastIndex(input, "}") + 1