diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md
index f8f4e856ab..fd3e76e406 100644
--- a/docs/docs/apis/proto/admin.md
+++ b/docs/docs/apis/proto/admin.md
@@ -430,6 +430,19 @@ and adds the user to the orgs members as ORG_OWNER
POST: /orgs/_setup
+### RemoveOrg
+
+> **rpc** RemoveOrg([RemoveOrgRequest](#removeorgrequest))
+[RemoveOrgResponse](#removeorgresponse)
+
+Sets the state of the organisation and all its resource (Users, Projects, Grants to and from the org) to removed
+Users of this organisation will not be able login
+
+
+
+ DELETE: /orgs/{org_id}
+
+
### GetIDPByID
> **rpc** GetIDPByID([GetIDPByIDRequest](#getidpbyidrequest))
@@ -3533,6 +3546,28 @@ This is an empty request
+### RemoveOrgRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| org_id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### RemoveOrgResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
### RemoveSMSProviderRequest
diff --git a/docs/docs/apis/proto/org.md b/docs/docs/apis/proto/org.md
index 8f9895093f..ba28470deb 100644
--- a/docs/docs/apis/proto/org.md
+++ b/docs/docs/apis/proto/org.md
@@ -95,6 +95,18 @@ title: zitadel/org.proto
| ----- | ---- | ----------- | ----------- |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.name_query | OrgNameQuery | - | |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.domain_query | OrgDomainQuery | - | |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.state_query | OrgStateQuery | - | |
+
+
+
+
+### OrgStateQuery
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| state | OrgState | - | enum.defined_only: true
|
diff --git a/internal/api/grpc/admin/org.go b/internal/api/grpc/admin/org.go
index b3b378461f..4874b01711 100644
--- a/internal/api/grpc/admin/org.go
+++ b/internal/api/grpc/admin/org.go
@@ -27,6 +27,16 @@ func (s *Server) SetDefaultOrg(ctx context.Context, req *admin_pb.SetDefaultOrgR
}, nil
}
+func (s *Server) RemoveOrg(ctx context.Context, req *admin_pb.RemoveOrgRequest) (*admin_pb.RemoveOrgResponse, error) {
+ details, err := s.command.RemoveOrg(ctx, req.OrgId)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.RemoveOrgResponse{
+ Details: object.DomainToChangeDetailsPb(details),
+ }, nil
+}
+
func (s *Server) GetDefaultOrg(ctx context.Context, _ *admin_pb.GetDefaultOrgRequest) (*admin_pb.GetDefaultOrgResponse, error) {
org, err := s.query.OrgByID(ctx, true, authz.GetInstance(ctx).DefaultOrganisationID())
return &admin_pb.GetDefaultOrgResponse{Org: org_grpc.OrgToPb(org)}, err
diff --git a/internal/api/grpc/org/converter.go b/internal/api/grpc/org/converter.go
index acd1c1c054..f98c97e4ff 100644
--- a/internal/api/grpc/org/converter.go
+++ b/internal/api/grpc/org/converter.go
@@ -25,6 +25,8 @@ func OrgQueryToModel(apiQuery *org_pb.OrgQuery) (query.SearchQuery, error) {
return query.NewOrgDomainSearchQuery(object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain)
case *org_pb.OrgQuery_NameQuery:
return query.NewOrgNameSearchQuery(object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name)
+ case *org_pb.OrgQuery_StateQuery:
+ return query.NewOrgStateSearchQuery(int32(q.StateQuery.State))
default:
return nil, errors.ThrowInvalidArgument(nil, "ORG-vR9nC", "List.Query.Invalid")
}
diff --git a/internal/query/org.go b/internal/query/org.go
index 4ddd71a023..d81c8c1d62 100644
--- a/internal/query/org.go
+++ b/internal/query/org.go
@@ -215,6 +215,10 @@ func NewOrgNameSearchQuery(method TextComparison, value string) (SearchQuery, er
return NewTextQuery(OrgColumnName, value, method)
}
+func NewOrgStateSearchQuery(value int32) (SearchQuery, error) {
+ return NewNumberQuery(OrgColumnState, value, NumberEquals)
+}
+
func NewOrgIDsSearchQuery(ids ...string) (SearchQuery, error) {
list := make([]interface{}, len(ids))
for i, value := range ids {
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index e57780ff42..61a1821ad0 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -613,6 +613,41 @@ service AdminService {
};
}
+
+ // Sets the state of the organisation and all its resource (Users, Projects, Grants to and from the org) to removed
+ // Users of this organisation will not be able login
+ rpc RemoveOrg(RemoveOrgRequest) returns (RemoveOrgResponse) {
+ option (google.api.http) = {
+ delete: "/orgs/{org_id}"
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "orgs";
+ tags: "global";
+ responses: {
+ key: "200";
+ value: {
+ description: "org removed successfully";
+ };
+ };
+ responses: {
+ key: "400";
+ value: {
+ description: "invalid org";
+ schema: {
+ json_schema: {
+ ref: "#/definitions/rpcStatus";
+ };
+ };
+ };
+ };
+ };
+ }
+
+
// Returns a identity provider configuration of the IAM instance
rpc GetIDPByID(GetIDPByIDRequest) returns (GetIDPByIDResponse) {
option (google.api.http) = {
@@ -3018,6 +3053,28 @@ message SetUpOrgResponse {
string user_id = 3;
}
+message RemoveOrgRequest {
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+ json_schema: {
+ required: ["org_id"]
+ };
+ };
+
+ string org_id = 1 [
+ (validate.rules).string = {min_len: 1, max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"69629023906488334\"";
+ min_length: 1;
+ max_length: 200;
+ }
+ ];
+}
+
+message RemoveOrgResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+
message GetIDPByIDRequest {
string id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
diff --git a/proto/zitadel/org.proto b/proto/zitadel/org.proto
index ef278724c6..d65b58609b 100644
--- a/proto/zitadel/org.proto
+++ b/proto/zitadel/org.proto
@@ -80,6 +80,7 @@ message OrgQuery {
OrgNameQuery name_query = 1;
OrgDomainQuery domain_query = 2;
+ OrgStateQuery state_query = 3;
}
}
@@ -113,6 +114,15 @@ message OrgDomainQuery {
];
}
+message OrgStateQuery {
+ OrgState state = 1 [
+ (validate.rules).enum.defined_only = true,
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "current state of the organisation";
+ }
+ ];
+}
+
enum OrgFieldName {
ORG_FIELD_NAME_UNSPECIFIED = 0;
ORG_FIELD_NAME_NAME = 1;