Skip to content

Commit 02e3241

Browse files
committed
[server, dashboard] Introduce "enable_multi_org" feature flag to allow admin-user to create organizations
1 parent ae26fdf commit 02e3241

File tree

6 files changed

+47
-7
lines changed

6 files changed

+47
-7
lines changed

components/dashboard/src/data/featureflag-query.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const featureFlags = {
2121
repositoryFinderSearch: false,
2222
// dummy specified dataops feature, default false
2323
dataops: false,
24+
enable_multi_org: false,
2425
showBrowserExtensionPromotion: false,
2526
enable_experimental_jbtb: false,
2627
enabled_configuration_prebuild_full_clone: false,

components/dashboard/src/menu/OrganizationSelector.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { useCurrentOrg, useOrganizations } from "../data/organizations/orgs-quer
1212
import { useLocation } from "react-router";
1313
import { useOrgBillingMode } from "../data/billing-mode/org-billing-mode-query";
1414
import { useIsOwner, useListOrganizationMembers, useHasRolePermission } from "../data/organizations/members-query";
15-
import { isOrganizationOwned } from "@gitpod/public-api-common/lib/user-utils";
15+
import { isAllowedToCreateOrganization } from "@gitpod/public-api-common/lib/user-utils";
1616
import { OrganizationRole } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
1717
import { useInstallationConfiguration } from "../data/installation/default-workspace-image-query";
18+
import { useFeatureFlag } from "../data/featureflag-query";
1819

1920
export default function OrganizationSelector() {
2021
const user = useCurrentUser();
@@ -27,9 +28,10 @@ export default function OrganizationSelector() {
2728
const getOrgURL = useGetOrgURL();
2829
const { data: installationConfig } = useInstallationConfiguration();
2930
const isDedicated = !!installationConfig?.isDedicatedInstallation;
31+
const isMultiOrgEnabled = useFeatureFlag("enable_multi_org");
3032

3133
// we should have an API to ask for permissions, until then we duplicate the logic here
32-
const canCreateOrgs = user && !isOrganizationOwned(user) && !isDedicated;
34+
const canCreateOrgs = user && isAllowedToCreateOrganization(user, isDedicated, isMultiOrgEnabled);
3335

3436
const userFullName = user?.name || "...";
3537

components/public-api/typescript-common/src/user-utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,24 @@ export function getName(user: User | UserProtocol): string | undefined {
7373
export function isOrganizationOwned(user: User | UserProtocol) {
7474
return !!user.organizationId;
7575
}
76+
77+
/**
78+
* gitpod.io: Only installation-level users are allowed to create orgs (installation-level users on gitpod.io, and admin user on Dedicated)
79+
* Dedicated: Only if multiOrg is enabled, installation-level users can create orgs
80+
* @param user
81+
* @param isDedicated
82+
* @param isMultiOrgEnabled
83+
* @returns
84+
*/
85+
export function isAllowedToCreateOrganization(
86+
user: User | UserProtocol,
87+
isDedicated: boolean,
88+
isMultiOrgEnabled?: boolean,
89+
): boolean {
90+
if (!isDedicated) {
91+
// gitpod.io case
92+
return !isOrganizationOwned(user);
93+
}
94+
95+
return !isOrganizationOwned(user) && !!isMultiOrgEnabled;
96+
}

components/server/src/orgs/organization-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export class OrganizationService {
152152
if (!user) {
153153
throw new ApplicationError(ErrorCodes.NOT_AUTHENTICATED, `User not authenticated. Please login.`);
154154
}
155-
const mayCreateOrganization = await this.userAuthentication.mayCreateOrJoinOrganization(user);
155+
const mayCreateOrganization = await this.userAuthentication.mayCreateOrganization(user);
156156
if (!mayCreateOrganization) {
157157
throw new ApplicationError(
158158
ErrorCodes.PERMISSION_DENIED,

components/server/src/user/user-authentication.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { EmailAddressAlreadyTakenException, SelectAccountException } from "../au
1616
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth";
1717
import { UserService } from "./user-service";
1818
import { Authorizer } from "../authorization/authorizer";
19+
import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
20+
import { isOrganizationOwned, isAllowedToCreateOrganization } from "@gitpod/public-api-common/lib/user-utils";
1921

2022
export interface CreateUserParams {
2123
organizationId?: string;
@@ -193,12 +195,26 @@ export class UserAuthentication {
193195
}
194196

195197
/**
196-
* Only installation-level users are allowed to create/join other orgs then the one they belong to
198+
* Only installation-level users are allowed to join other orgs then the one they belong to
197199
* @param user
198200
* @returns
199201
*/
200-
async mayCreateOrJoinOrganization(user: User): Promise<boolean> {
201-
return !user.organizationId;
202+
async mayJoinOrganization(user: User): Promise<boolean> {
203+
return !isOrganizationOwned(user);
204+
}
205+
206+
/**
207+
* gitpod.io: Only installation-level users are allowed to create orgs
208+
* Dedicated: Only if multiOrg is enabled, installation-level users (=admin-user) can create orgs
209+
* @param user
210+
* @returns
211+
*/
212+
async mayCreateOrganization(user: User): Promise<boolean> {
213+
const isDedicated = this.config.isDedicatedInstallation;
214+
const isMultiOrgEnabled = await getExperimentsClientForBackend().getValueAsync("enable_multi_org", false, {
215+
gitpodHost: this.config.hostUrl.url.host,
216+
});
217+
return isAllowedToCreateOrganization(user, isDedicated, isMultiOrgEnabled);
202218
}
203219

204220
async isBlocked(params: CheckIsBlockedParams): Promise<boolean> {

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
14461446

14471447
const user = await this.checkAndBlockUser("joinTeam");
14481448

1449-
const mayCreateOrganization = await this.userAuthentication.mayCreateOrJoinOrganization(user);
1449+
const mayCreateOrganization = await this.userAuthentication.mayJoinOrganization(user);
14501450
if (!mayCreateOrganization) {
14511451
throw new ApplicationError(
14521452
ErrorCodes.PERMISSION_DENIED,

0 commit comments

Comments
 (0)