Skip to content

Commit

Permalink
✨ feat: workspace (featbit#534)
Browse files Browse the repository at this point in the history
* refactor: rename account to organization

* In progress: front-end

* In progress: back-end

* update init mongodb scripts

* feat: make workspace as the top level of entities

* rename to workspace

* in progress

* refactor

* in progress

* in progress

* sso

* put sso config in db

* fixed error

* sso license check

* in progress

* i18n

* Update workspace.component.html

* in progress

* select org

* fixed error

* fixed error

* remove license check for schedule worker

* Fixed tests

* configmap

* set menu icon

* update sso

* re arrangement

* small fixes

* rename license create-org to multiple-organization

* set sso default to false

* add ApiConstants

* delete OidcOptions

* fixed test

* added sso env var into docker compose files

* merge fix

* sso can select org when first login

* remove unused parameter

* refactor

* rename

* rename

* refactor SsoController

* update error message

* remove extra error codes

* rename

* format

* chore

* chore

* refactor LoginByEmail and UserService

* pr feedback

* workspace key is optional

* fix tests

* chore

* refactor AddUser

* remove extra error codes

* chore

* chore

* refactor IdentityService

* delete IUserStore

* change method signature

* refactor ILicenseService

* refactor IWorkspaceService

* more refactor

* chore

* fix bug

* update init.js

* chore

* update init monogo script

* refactor IdentityService

* chore

* rename auth to profile and fix a capability problem

* chore

* rename IAuthProps to IProfile

* chore

* update subscribe usage

* fix breadcrumb

* refactor workspace.component.ts

* chore

* fix

* update i18n

* is sso first login

* update test

* update sso setup doc

* fix the organization router link

---------

Co-authored-by: deleteLater <[email protected]>
  • Loading branch information
cosmos-explorer and deleteLater authored Nov 17, 2023
1 parent 2fb9f26 commit 6f8a3fe
Show file tree
Hide file tree
Showing 157 changed files with 3,655 additions and 1,673 deletions.
25 changes: 12 additions & 13 deletions docker/composes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@ docker run -d -p 9000:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=ad
4. Set the client's `Web Origins` to `http://localhost:8081/*`
5. Enable **Client authentication**
6. Create a user
7. Replace the following config with your own value for Api projects' appsetting.json, then start Api service locally
7. Set environment variable `SSOEnabled` to `true`
8. Add the following OpenId Connect settings via UI (replace with your own values)

```json
"SSO": {
"enabled": true,
"OIDC": {
"clientId": "test-oidc-client",
"clientSecret": "tr8XwUrWo8U2wdJFb7EZ5HbqVWZEns5V",
"tokenEndpoint": "http://localhost:9000/realms/featbit/protocol/openid-connect/token",
"clientAuthenticationMethod": "client_secret_post",
"authorizationEndpoint": "http://localhost:9000/realms/featbit/protocol/openid-connect/auth",
"userEmailClaim": "email",
"scope": "openid profile email"
}
}
{
"clientId": "test-oidc-client",
"clientSecret": "tr8XwUrWo8U2wdJFb7EZ5HbqVWZEns5V",
"tokenEndpoint": "http://host.docker.internal:9000/realms/featbit/protocol/openid-connect/token",
"clientAuthenticationMethod": "client_secret_post",
"authorizationEndpoint": "http://localhost:9000/realms/featbit/protocol/openid-connect/auth",
"userEmailClaim": "email",
"scope": "openid profile email"
}
```
2 changes: 2 additions & 0 deletions docker/composes/docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ services:
build:
context: ./modules/back-end
dockerfile: ./deploy/Dockerfile
environment:
- SSOEnabled=true
depends_on:
- mongodb
- redis
Expand Down
1 change: 1 addition & 0 deletions docker/composes/docker-compose-pro-dev-HA-kafka.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ services:
dockerfile: ./deploy/Dockerfile
environment:
- IS_PRO=true
- SSOEnabled=false
- KAFKA__BOOTSTRAPSERVERS=kafka:9092
- KAFKA__CONSUMERSERVERS=kafka-consumer:9092
depends_on:
Expand Down
1 change: 1 addition & 0 deletions docker/composes/docker-compose-pro-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ services:
dockerfile: ./deploy/Dockerfile
environment:
- IS_PRO=true
- SSOEnabled=false
depends_on:
mongodb:
condition: service_started
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ services:
- Redis__ConnectionString=redis:6379
- OLAP__ServiceHost=http://da-server
- IS_PRO=true
- SSOEnabled=false
depends_on:
mongodb:
condition: service_started
Expand Down
3 changes: 2 additions & 1 deletion docker/composes/docker-compose-redis-cluster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ services:
- MongoDb__ConnectionString=mongodb://admin:password@mongodb:27017
- MongoDb__Database=featbit
- Redis__ConnectionString=redis-node-5:6379,redis-node-4:6379,redis-node-3:6379,redis-node-2:6379,redis-node-1:6379,redis-node-0:6379,ssl=false,password=bitnami
- OLAP__ServiceHost=http://da-server
- OLAP__ServiceHost=http://da-server
- SSOEnabled=false
depends_on:
- mongodb
- redis-node-5
Expand Down
3 changes: 2 additions & 1 deletion docker/composes/docker-compose-redis-sentinel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ services:
- MongoDb__ConnectionString=mongodb://admin:password@mongodb:27017
- MongoDb__Database=featbit
- Redis__ConnectionString=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379,serviceName=mymaster,defaultDatabase=1,ssl=false,password=featbit,user=featbit
- OLAP__ServiceHost=http://da-server
- OLAP__ServiceHost=http://da-server
- SSOEnabled=false
depends_on:
- mongodb
- redis-sentinel-1
Expand Down
1 change: 1 addition & 0 deletions docker/composes/docker-compose-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ services:
- MongoDb__Database=featbit
- Kafka__BootstrapServers=kafka:9092
- OLAP__ServiceHost=http://da-server
- SSOEnabled=false
ports:
- "5000:5000"
networks:
Expand Down
19 changes: 19 additions & 0 deletions infra/mongodb/docker-entrypoint-initdb.d/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ db = db.getSiblingDB(dbName)
print('seed started...')

// seed ids
const workspaceId = UUID()
const userId = UUID()
const organizationId = UUID()

Expand All @@ -18,6 +19,22 @@ function getUUIDString() {
return UUID().toString().split('"')[1];
}

// seed workspace
print('clean and seed collection: Workspaces')
db.Workspaces.deleteMany({})
db.Workspaces.insertOne(
{
_id: workspaceId,
name: "Default Workspace",
key: "default-workspace",
sso: null,
license: null,
createdAt: new Date(),
updatedAt: new Date()
}
)
print('collection seeded: Workspaces')

// seed user
print('clean and seed collection: Users')
db.Users.deleteMany({})
Expand All @@ -28,6 +45,7 @@ db.Users.insertOne(
password: "AQAAAAEAACcQAAAAELDHEjCrDQrmnAXU5C//mOLvUBJ7lnVFEMMFxNMDIIrF7xK8JDQKUifU3HH4gexNAQ==",
name: "tester",
origin: "Local",
workspaceId: workspaceId,
createAt: new Date(),
updatedAt: new Date()
}
Expand All @@ -40,6 +58,7 @@ db.Organizations.deleteMany({})
db.Organizations.insertOne(
{
_id: organizationId,
workspaceId: workspaceId,
name: "playground",
initialized: false,
createdAt: new Date(),
Expand Down
19 changes: 19 additions & 0 deletions kubernetes/pro/infrastructure/mongodb-init-configMap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ data:
print('seed started...')
// seed ids
const workspaceId = UUID()
const userId = UUID()
const organizationId = UUID()
Expand All @@ -26,6 +27,22 @@ data:
return UUID().toString().split('"')[1];
}
// seed workspace
print('clean and seed collection: Workspaces')
db.Workspaces.deleteMany({})
db.Workspaces.insertOne(
{
_id: workspaceId,
name: "Default Workspace",
key: "default-workspace",
sso: null,
license: null,
createdAt: new Date(),
updatedAt: new Date()
}
)
print('collection seeded: Workspaces')
// seed user
print('clean and seed collection: Users')
db.Users.deleteMany({})
Expand All @@ -36,6 +53,7 @@ data:
password: "AQAAAAEAACcQAAAAELDHEjCrDQrmnAXU5C//mOLvUBJ7lnVFEMMFxNMDIIrF7xK8JDQKUifU3HH4gexNAQ==",
name: "tester",
origin: "Local",
workspaceId: workspaceId,
createAt: new Date(),
updatedAt: new Date()
}
Expand All @@ -48,6 +66,7 @@ data:
db.Organizations.insertOne(
{
_id: organizationId,
workspaceId: workspaceId,
name: "playground",
initialized: false,
createdAt: new Date(),
Expand Down
19 changes: 19 additions & 0 deletions kubernetes/standard/infrastructure/mongodb-init-configMap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ data:
print('seed started...')
// seed ids
const workspaceId = UUID()
const userId = UUID()
const organizationId = UUID()
Expand All @@ -26,6 +27,22 @@ data:
return UUID().toString().split('"')[1];
}
// seed workspace
print('clean and seed collection: Workspaces')
db.Workspaces.deleteMany({})
db.Workspaces.insertOne(
{
_id: workspaceId,
name: "Default Workspace",
key: "default-workspace",
sso: null,
license: null,
createdAt: new Date(),
updatedAt: new Date()
}
)
print('collection seeded: Workspaces')
// seed user
print('clean and seed collection: Users')
db.Users.deleteMany({})
Expand All @@ -36,6 +53,7 @@ data:
password: "AQAAAAEAACcQAAAAELDHEjCrDQrmnAXU5C//mOLvUBJ7lnVFEMMFxNMDIIrF7xK8JDQKUifU3HH4gexNAQ==",
name: "tester",
origin: "Local",
workspaceId: workspaceId,
createAt: new Date(),
updatedAt: new Date()
}
Expand All @@ -48,6 +66,7 @@ data:
db.Organizations.insertOne(
{
_id: organizationId,
workspaceId: workspaceId,
name: "playground",
initialized: false,
createdAt: new Date(),
Expand Down
8 changes: 8 additions & 0 deletions modules/back-end/src/Api/ApiConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Api;

public static class ApiConstants
{
public const string OrgIdHeaderKey = "Organization";

public const string WorkspaceHeaderKey = "Workspace";
}
2 changes: 0 additions & 2 deletions modules/back-end/src/Api/Authentication/OpenApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ public static class OpenApiConstants
{
public const string PermissionStoreKey = "pemissions";

public const string OrgIdHeaderKey = "Organization";

public const string ApiGroupName = "OpenApi";
}
9 changes: 7 additions & 2 deletions modules/back-end/src/Api/Authentication/OpenApiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Api.Authentication;

public class OpenApiHandler : AuthenticationHandler<OpenApiOptions>
{
private readonly IOrganizationService _organizationService;
private readonly IAccessTokenService _accessTokenService;
private readonly IMemberService _memberService;

Expand All @@ -18,9 +19,11 @@ public OpenApiHandler(
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IOrganizationService organizationService,
IAccessTokenService accessTokenService,
IMemberService memberService) : base(options, logger, encoder, clock)
{
_organizationService = organizationService;
_accessTokenService = accessTokenService;
_memberService = memberService;
}
Expand All @@ -44,8 +47,10 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
return AuthenticateResult.Fail("invalid-access-token");
}

// set organization id header & store permissions
Context.Request.Headers.Add(OpenApiConstants.OrgIdHeaderKey, accessToken.OrganizationId.ToString());
// set workspace, organization id header & store permissions
var org = await _organizationService.GetAsync(accessToken.OrganizationId);
Context.Request.Headers.Add(ApiConstants.WorkspaceHeaderKey, org.WorkspaceId.ToString());
Context.Request.Headers.Add(ApiConstants.OrgIdHeaderKey, org.ToString());
if (accessToken.Type == AccessTokenTypes.Service)
{
Context.Items[OpenApiConstants.PermissionStoreKey] = accessToken.Permissions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text;
using Application.Identity;
using Domain.Workspaces;

namespace Api.Authentication.OpenIdConnect;

public interface IClientAuthenticator
{
AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions options);
AuthParameters GetAuthParameters(LoginByOidcCode request, OidcConfig config);
}

public static class ClientAuthenticator
Expand All @@ -23,7 +24,7 @@ public static IClientAuthenticator GetAuthenticator(string method)

public class BasicAuthenticator : IClientAuthenticator
{
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions options)
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcConfig config)
{
var kvs = new List<KeyValuePair<string, string>>
{
Expand All @@ -33,7 +34,7 @@ public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions opt
};
var httpContent = new FormUrlEncodedContent(kvs);

var client = $"{options.ClientId}:{options.ClientSecret}";
var client = $"{config.ClientId}:{config.ClientSecret}";
var authorizationString = Convert.ToBase64String(Encoding.UTF8.GetBytes(client));

var param = new AuthParameters(httpContent, authorizationString);
Expand All @@ -43,15 +44,15 @@ public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions opt

public class PostAuthenticator : IClientAuthenticator
{
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions options)
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcConfig config)
{
var kvs = new List<KeyValuePair<string, string>>
{
new("code", request.Code),
new("grant_type", "authorization_code"),
new("redirect_uri", request.RedirectUri),
new("client_id", options.ClientId),
new("client_secret", options.ClientSecret),
new("client_id", config.ClientId),
new("client_secret", config.ClientSecret),
};
var httpContent = new FormUrlEncodedContent(kvs);

Expand All @@ -62,7 +63,7 @@ public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions opt

public class NoneAuthenticator : IClientAuthenticator
{
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcOptions options)
public AuthParameters GetAuthParameters(LoginByOidcCode request, OidcConfig config)
{
var kvs = new List<KeyValuePair<string, string>>
{
Expand Down
Loading

0 comments on commit 6f8a3fe

Please sign in to comment.