Compare commits

...

10 Commits

Author SHA1 Message Date
x032205
510caa9c8a Reorder alphabetically 2025-07-10 12:19:46 -04:00
Sid
adc90a912f Update backend/src/lib/config/env.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-07-10 21:05:58 +05:30
Sid
d19a9c1643 Update backend/src/lib/config/env.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-07-10 21:05:37 +05:30
sidwebworks
da8fa720ae feat: add more env overrides 2025-07-10 20:59:47 +05:30
x032205
da82cfdf6b Merge pull request #3925 from Infisical/ENG-3041
feat(secret-scanning): Bitbucket data source + App Connection
2025-07-07 22:41:38 -04:00
Scott Wilson
bf707667b5 Merge pull request #3977 from Infisical/fix-search-filter-for-imported-secrets-on-single-env-view
fix(secret-imports-dashboard): support filtering imported secrets in single env view
2025-07-07 18:32:20 -07:00
Scott Wilson
d2e6743f22 fix: support filtering imported secrets in singl env view 2025-07-07 18:06:09 -07:00
x032205
0191eb48f3 Merge pull request #3974 from Infisical/fix-email-invite-notifications
Improve + fix invitation reminder logic
2025-07-07 14:47:50 -04:00
Carlos Monastyrski
9d39910152 Minor fix to prevent setting lastInvitedAt for invitees who weren’t actually sent an invitation 2025-07-07 15:35:49 -03:00
x032205
9137fa4ca5 Improve + fix invitation reminder logic 2025-07-07 13:31:20 -04:00
6 changed files with 185 additions and 30 deletions

View File

@@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt");
if (hasColumn) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.datetime("lastInvitedAt").nullable().defaultTo(knex.fn.now()).alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt");
if (hasColumn) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.datetime("lastInvitedAt").nullable().alter();
});
}
}

View File

@@ -373,6 +373,19 @@ export const overwriteSchema: {
fields: { key: keyof TEnvConfig; description?: string }[];
};
} = {
aws: {
name: "AWS",
fields: [
{
key: "INF_APP_CONNECTION_AWS_ACCESS_KEY_ID",
description: "The Access Key ID of your AWS account."
},
{
key: "INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY",
description: "The Client Secret of your AWS application."
}
]
},
azure: {
name: "Azure",
fields: [
@@ -386,16 +399,79 @@ export const overwriteSchema: {
}
]
},
google_sso: {
name: "Google SSO",
gcp: {
name: "GCP",
fields: [
{
key: "CLIENT_ID_GOOGLE_LOGIN",
description: "The Client ID of your GCP OAuth2 application."
key: "INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL",
description: "The GCP Service Account JSON credentials."
}
]
},
github_app: {
name: "GitHub App",
fields: [
{
key: "INF_APP_CONNECTION_GITHUB_APP_CLIENT_ID",
description: "The Client ID of your GitHub application."
},
{
key: "CLIENT_SECRET_GOOGLE_LOGIN",
description: "The Client Secret of your GCP OAuth2 application."
key: "INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET",
description: "The Client Secret of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_APP_SLUG",
description: "The Slug of your GitHub application. This is the one found in the URL."
},
{
key: "INF_APP_CONNECTION_GITHUB_APP_ID",
description: "The App ID of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY",
description: "The Private Key of your GitHub application."
}
]
},
github_oauth: {
name: "GitHub OAuth",
fields: [
{
key: "INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID",
description: "The Client ID of your GitHub OAuth application."
},
{
key: "INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_SECRET",
description: "The Client Secret of your GitHub OAuth application."
}
]
},
github_radar_app: {
name: "GitHub Radar App",
fields: [
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_ID",
description: "The Client ID of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_SECRET",
description: "The Client Secret of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_SLUG",
description: "The Slug of your GitHub application. This is the one found in the URL."
},
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_ID",
description: "The App ID of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_PRIVATE_KEY",
description: "The Private Key of your GitHub application."
},
{
key: "INF_APP_CONNECTION_GITHUB_RADAR_APP_WEBHOOK_SECRET",
description: "The Webhook Secret of your GitHub application."
}
]
},
@@ -412,6 +488,19 @@ export const overwriteSchema: {
}
]
},
gitlab_oauth: {
name: "GitLab OAuth",
fields: [
{
key: "INF_APP_CONNECTION_GITLAB_OAUTH_CLIENT_ID",
description: "The Client ID of your GitLab OAuth application."
},
{
key: "INF_APP_CONNECTION_GITLAB_OAUTH_CLIENT_SECRET",
description: "The Client Secret of your GitLab OAuth application."
}
]
},
gitlab_sso: {
name: "GitLab SSO",
fields: [
@@ -429,6 +518,19 @@ export const overwriteSchema: {
"The URL of your self-hosted instance of GitLab where the OAuth application is registered. If no URL is passed in, this will default to https://gitlab.com."
}
]
},
google_sso: {
name: "Google SSO",
fields: [
{
key: "CLIENT_ID_GOOGLE_LOGIN",
description: "The Client ID of your GCP OAuth2 application."
},
{
key: "CLIENT_SECRET_GOOGLE_LOGIN",
description: "The Client Secret of your GCP OAuth2 application."
}
]
}
};

View File

@@ -732,8 +732,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId,
projectId,
environment,
path: secretPath,
search
path: secretPath
// search scott: removing for now because this prevents searching imported secrets which are fetched separately client side
});
if (remainingLimit > 0 && totalImportCount > adjustedOffset) {
@@ -745,7 +745,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
projectId,
environment,
path: secretPath,
search,
// search scott: removing for now because this prevents searching imported secrets which are fetched separately client side
limit: remainingLimit,
offset: adjustedOffset
});

View File

@@ -122,8 +122,8 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
.orWhere((qb) => {
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
void qb
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneMonthAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneWeekAgo);
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneWeekAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneMonthAgo);
});
return memberships;
@@ -135,9 +135,22 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
}
};
const updateLastInvitedAtByIds = async (membershipIds: string[]) => {
try {
if (membershipIds.length === 0) return;
await db(TableName.OrgMembership).whereIn("id", membershipIds).update({ lastInvitedAt: new Date() });
} catch (error) {
throw new DatabaseError({
error,
name: "Update last invited at by ids"
});
}
};
return {
...orgMembershipOrm,
findOrgMembershipById,
findRecentInvitedMemberships
findRecentInvitedMemberships,
updateLastInvitedAtByIds
};
};

View File

@@ -109,7 +109,12 @@ type TOrgServiceFactoryDep = {
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey" | "create">;
orgMembershipDAL: Pick<
TOrgMembershipDALFactory,
"findOrgMembershipById" | "findOne" | "findById" | "findRecentInvitedMemberships" | "updateById"
| "findOrgMembershipById"
| "findOne"
| "findById"
| "findRecentInvitedMemberships"
| "updateById"
| "updateLastInvitedAtByIds"
>;
incidentContactDAL: TIncidentContactsDALFactory;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne">;
@@ -763,6 +768,10 @@ export const orgServiceFactory = ({
}
});
await orgMembershipDAL.updateById(inviteeOrgMembership.id, {
lastInvitedAt: new Date()
});
return { signupToken: undefined };
};
@@ -1433,6 +1442,7 @@ export const orgServiceFactory = ({
const appCfg = getConfig();
const orgCache: Record<string, { name: string; id: string } | undefined> = {};
const notifiedUsers: string[] = [];
await Promise.all(
invitedUsers.map(async (invitedUser) => {
@@ -1463,13 +1473,12 @@ export const orgServiceFactory = ({
callback_url: `${appCfg.SITE_URL}/signupinvite`
}
});
notifiedUsers.push(invitedUser.id);
}
await orgMembershipDAL.updateById(invitedUser.id, {
lastInvitedAt: new Date()
});
})
);
await orgMembershipDAL.updateLastInvitedAtByIds(notifiedUsers);
};
return {

View File

@@ -9,6 +9,7 @@ import {
faInfoCircle,
faKey,
faRotate,
faSearch,
faUpDown,
faWarning,
faXmark
@@ -140,6 +141,10 @@ export const SecretImportItem = ({
}
};
const filteredImportedSecrets = importedSecrets.filter((secret) =>
secret.key.toUpperCase().includes(searchTerm.toUpperCase())
);
return (
<>
<div
@@ -305,21 +310,26 @@ export const SecretImportItem = ({
</td>
</tr>
)}
{importedSecrets
.filter((secret) => secret.key.toUpperCase().includes(searchTerm.toUpperCase()))
.map(({ key, value }, index) => (
<tr key={`${id}-${key}-${index + 1}`}>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
{key}
</td>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
<SecretInput value={value} isReadOnly />
</td>
{/* <td className="h-10" style={{ padding: "0.25rem 1rem" }}>
{filteredImportedSecrets.length === 0 && importedSecrets?.length !== 0 && (
<tr>
<td colSpan={3}>
<EmptyState title="No secrets match search" icon={faSearch} />
</td>
</tr>
)}
{filteredImportedSecrets.map(({ key, value }, index) => (
<tr key={`${id}-${key}-${index + 1}`}>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
{key}
</td>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
<SecretInput value={value} isReadOnly />
</td>
{/* <td className="h-10" style={{ padding: "0.25rem 1rem" }}>
<EnvFolderIcon env={overriden?.env} secretPath={overriden?.secretPath} />
</td> */}
</tr>
))}
</tr>
))}
</tbody>
</table>
</TableContainer>