diff --git a/docs/changelog/overview.mdx b/docs/changelog/overview.mdx index d265b546c..b66b07288 100644 --- a/docs/changelog/overview.mdx +++ b/docs/changelog/overview.mdx @@ -4,6 +4,30 @@ title: "Changelog" The changelog below reflects new product developments and updates on a monthly basis. +## July 2024 +- Released the official [Ruby SDK](https://infisical.com/docs/sdks/languages/ruby). +- Increased the speed and efficiency of secret operations. +- Released AWS KMS wrapping (bring your own key). +- Users can now log in to CLI via SSO in non-browser environments. +- Released [Slack Webhooks](https://infisical.com/docs/documentation/platform/webhooks). +- Added [Dynamic Secrets with MS SQL](https://infisical.com/docs/documentation/platform/dynamic-secrets/mssql). +- Redesigned and simplified the Machine Identities page. +- Added the ability to move secrets/folders to another location. +- Added [OIDC](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general) support to CLI, Go SDK, and more. +- Released [Linux installer for Infisical](https://infisical.com/docs/self-hosting/deployment-options/native/standalone-binary). + +## June 2024 +- Released [Infisical PKI](https://infisical.com/docs/documentation/platform/pki/overview). +- Released the official [Go SDK](https://infisical.com/docs/sdks/languages/go). +- Released [OIDC Authentication method](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general). +- Allowed users to configure log retention periods on self-hosted instances. +- Added [tags](https://registry.terraform.io/providers/Infisical/infisical/latest/docs/resources/secret_tag) to terraform provider. +- Released [public secret sharing](https://share.infisical.com). +- Built a [native integration with Rundeck](https://infisical.com/docs/integrations/cicd/rundeck). +- Added list view for projects in the dashboard. +- Fixed offline coding mode in CLI. +- Users are now able to leave a particular project themselves. + ## May 2024 - Released [AWS](https://infisical.com/docs/documentation/platform/identities/aws-auth), [GCP](https://infisical.com/docs/documentation/platform/identities/gcp-auth), [Azure](https://infisical.com/docs/documentation/platform/identities/azure-auth), and [Kubernetes](https://infisical.com/docs/documentation/platform/identities/kubernetes-auth) Native Auth Methods. - Added [Secret Sharing](https://infisical.com/docs/documentation/platform/secret-sharing) functionality for sharing sensitive data through encrypted links – within and outside of an organization. diff --git a/frontend/src/views/Org/IdentityPage/IdentityPage.tsx b/frontend/src/views/Org/IdentityPage/IdentityPage.tsx index 1be789baa..3d863f2d3 100644 --- a/frontend/src/views/Org/IdentityPage/IdentityPage.tsx +++ b/frontend/src/views/Org/IdentityPage/IdentityPage.tsx @@ -24,6 +24,7 @@ import { useRevokeIdentityTokenAuthToken, useRevokeIdentityUniversalAuthClientSecret} from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; +import { TabSections } from"@app/views/Org/Types"; import { IdentityAuthMethodModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal"; import { IdentityModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal"; @@ -75,7 +76,7 @@ export const IdentityPage = withPermission( }); handlePopUpClose("deleteIdentity"); - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Identities}`); } catch (err) { console.error(err); const error = err as any; @@ -154,7 +155,7 @@ export const IdentityPage = withPermission( type="submit" leftIcon={} onClick={() => { - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Identities}`); }} className="mb-4" > diff --git a/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectRow.tsx b/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectRow.tsx index e5ec47f6c..70a3c998b 100644 --- a/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectRow.tsx +++ b/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectRow.tsx @@ -10,6 +10,7 @@ import { useWorkspace } from "@app/context"; import { IdentityMembership } from "@app/hooks/api/identities/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; +import { TabSections } from "@app/views/Org/Types"; type Props = { membership: IdentityMembership; @@ -51,7 +52,7 @@ export const IdentityProjectRow = ({ key={`identity-project-membership-${id}`} onClick={() => { if (isAccessible) { - router.push(`/project/${project.id}/members`); + router.push(`/project/${project.id}/members?selectedTab=${TabSections.Identities}`); return; } diff --git a/frontend/src/views/Org/MembersPage/MembersPage.tsx b/frontend/src/views/Org/MembersPage/MembersPage.tsx index 0a9363d09..303411ab0 100644 --- a/frontend/src/views/Org/MembersPage/MembersPage.tsx +++ b/frontend/src/views/Org/MembersPage/MembersPage.tsx @@ -1,23 +1,39 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; + import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { withPermission } from "@app/hoc"; +import { isTabSection, TabSections } from "@app/views/Org/Types";; import { OrgIdentityTab, OrgMembersTab, OrgRoleTabSection } from "./components"; -enum TabSections { - Member = "members", - Roles = "roles", - Identities = "identities" -} - export const MembersPage = withPermission( () => { + const router = useRouter(); + const { query } = router; + const selectedTab = query.selectedTab as string; + const [activeTab, setActiveTab] = useState(TabSections.Member); + + useEffect(() => { + if (selectedTab && isTabSection(selectedTab)) { + setActiveTab(selectedTab); + } + }, [isTabSection, selectedTab]); + + const updateSelectedTab = (tab: string) => { + router.push({ + pathname: router.pathname, + query: { ...router.query, selectedTab: tab }, + }); + } + return ( Organization Access Control - + Users @@ -25,7 +41,7 @@ export const MembersPage = withPermission( Machine Identities - Organization Roles + Organization Roles diff --git a/frontend/src/views/Org/RolePage/RolePage.tsx b/frontend/src/views/Org/RolePage/RolePage.tsx index c8e78a54e..eb5dae788 100644 --- a/frontend/src/views/Org/RolePage/RolePage.tsx +++ b/frontend/src/views/Org/RolePage/RolePage.tsx @@ -19,6 +19,7 @@ import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@a import { withPermission } from "@app/hoc"; import { useDeleteOrgRole, useGetOrgRole } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; +import { TabSections } from "@app/views/Org/Types"; import { RoleDetailsSection, RoleModal, RolePermissionsSection } from "./components"; @@ -51,7 +52,7 @@ export const RolePage = withPermission( }); handlePopUpClose("deleteOrgRole"); - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Roles}`); } catch (err) { console.error(err); const error = err as any; @@ -75,7 +76,7 @@ export const RolePage = withPermission( type="submit" leftIcon={} onClick={() => { - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Roles}`); }} className="mb-4" > diff --git a/frontend/src/views/Org/Types/TabSections.ts b/frontend/src/views/Org/Types/TabSections.ts new file mode 100644 index 000000000..945370d45 --- /dev/null +++ b/frontend/src/views/Org/Types/TabSections.ts @@ -0,0 +1,9 @@ +export enum TabSections { + Member = "members", + Roles = "roles", + Identities = "identities" +} + +export const isTabSection = (value: string): value is TabSections => { + return (Object.values(TabSections) as string[]).includes(value); +} diff --git a/frontend/src/views/Org/Types/index.ts b/frontend/src/views/Org/Types/index.ts new file mode 100644 index 000000000..76f47c6f2 --- /dev/null +++ b/frontend/src/views/Org/Types/index.ts @@ -0,0 +1,3 @@ +import { TabSections, isTabSection } from "./TabSections"; + +export { TabSections, isTabSection }; diff --git a/frontend/src/views/Org/UserPage/UserPage.tsx b/frontend/src/views/Org/UserPage/UserPage.tsx index 2858d97df..f1ac3fae8 100644 --- a/frontend/src/views/Org/UserPage/UserPage.tsx +++ b/frontend/src/views/Org/UserPage/UserPage.tsx @@ -29,6 +29,7 @@ import { useUpdateOrgMembership } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; +import { TabSections } from "@app/views/Org/Types"; import { UserDetailsSection, UserOrgMembershipModal, UserProjectsSection } from "./components"; @@ -90,7 +91,7 @@ export const UserPage = withPermission( }); handlePopUpClose("removeMember"); - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Member}`); } catch (err) { console.error(err); createNotification({ @@ -111,7 +112,7 @@ export const UserPage = withPermission( type="submit" leftIcon={} onClick={() => { - router.push(`/org/${orgId}/members`); + router.push(`/org/${orgId}/members?selectedTab=${TabSections.Member}`); }} className="mb-4" > diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx index 31c3184af..0e13442a5 100644 --- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx +++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx @@ -9,6 +9,7 @@ import { useWorkspace } from "@app/context"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { TWorkspaceUser } from "@app/hooks/api/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; +import { TabSections } from "@app/views/Org/Types";; type Props = { membership: TWorkspaceUser; @@ -47,7 +48,7 @@ export const UserProjectRow = ({ key={`user-project-membership-${id}`} onClick={() => { if (isAccessible) { - router.push(`/project/${project.id}/members`); + router.push(`/project/${project.id}/members?selectedTab=${TabSections.Member}`); return; } diff --git a/frontend/src/views/Project/MembersPage/MembersPage.tsx b/frontend/src/views/Project/MembersPage/MembersPage.tsx index 188d11ede..5b30b1d4e 100644 --- a/frontend/src/views/Project/MembersPage/MembersPage.tsx +++ b/frontend/src/views/Project/MembersPage/MembersPage.tsx @@ -1,25 +1,40 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; + import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { withProjectPermission } from "@app/hoc"; import { IdentityTab, MembersTab,ProjectRoleListTab, ServiceTokenTab } from "./components"; +import { TabSections, isTabSection } from '../Types'; -enum TabSections { - Member = "members", - Roles = "roles", - Groups = "groups", - Identities = "identities", - ServiceTokens = "service-tokens" -} export const MembersPage = withProjectPermission( () => { + const router = useRouter(); + const { query } = router; + const selectedTab = query.selectedTab as string; + const [activeTab, setActiveTab] = useState(TabSections.Member); + + useEffect(() => { + if (selectedTab && isTabSection(selectedTab)) { + setActiveTab(selectedTab); + } + }, [isTabSection, selectedTab]); + + const updateSelectedTab = (tab: string) => { + router.push({ + pathname: router.pathname, + query: { ...router.query, selectedTab: tab }, + }); + } + return ( Project Access Control - + Users diff --git a/frontend/src/views/Project/RolePage/RolePage.tsx b/frontend/src/views/Project/RolePage/RolePage.tsx index 7e19b49f4..95ea64a90 100644 --- a/frontend/src/views/Project/RolePage/RolePage.tsx +++ b/frontend/src/views/Project/RolePage/RolePage.tsx @@ -21,6 +21,7 @@ import { useDeleteProjectRole,useGetProjectRoleBySlug } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; import { RoleDetailsSection, RoleModal, RolePermissionsSection } from "./components"; +import { TabSections } from '../Types'; export const RolePage = withProjectPermission( () => { @@ -52,7 +53,7 @@ export const RolePage = withProjectPermission( type: "success" }); handlePopUpClose("deleteRole"); - router.push(`/project/${projectId}/members`); + router.push(`/project/${projectId}/members?selectedTab=${TabSections.Roles}`); } catch (err) { console.error(err); const error = err as any; @@ -75,7 +76,7 @@ export const RolePage = withProjectPermission( variant="link" type="submit" leftIcon={} - onClick={() => router.push(`/project/${projectId}/members`)} + onClick={() => router.push(`/project/${projectId}/members?selectedTab=${TabSections.Roles}`)} className="mb-4" > Roles diff --git a/frontend/src/views/Project/Types/TabSections.ts b/frontend/src/views/Project/Types/TabSections.ts new file mode 100644 index 000000000..ffc93587e --- /dev/null +++ b/frontend/src/views/Project/Types/TabSections.ts @@ -0,0 +1,11 @@ +export enum TabSections { + Member = "members", + Roles = "roles", + Groups = "groups", + Identities = "identities", + ServiceTokens = "service-tokens" +} + +export const isTabSection = (value: string): value is TabSections => { + return (Object.values(TabSections) as string[]).includes(value); +} diff --git a/frontend/src/views/Project/Types/index.ts b/frontend/src/views/Project/Types/index.ts new file mode 100644 index 000000000..76f47c6f2 --- /dev/null +++ b/frontend/src/views/Project/Types/index.ts @@ -0,0 +1,3 @@ +import { TabSections, isTabSection } from "./TabSections"; + +export { TabSections, isTabSection };
Organization Access Control
Machine Identities
Project Access Control