From dac059a7f7d0177afb95decf5f5b0335cc014a5c Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 16 Jun 2025 23:56:28 +0800 Subject: [PATCH] refactor: identity provider service --- proto/api/v1/idp_service.proto | 87 +++++++--- proto/gen/api/v1/idp_service.pb.go | 150 +++++++++++++----- proto/gen/api/v1/idp_service.pb.gw.go | 28 ++++ proto/gen/apidocs.swagger.yaml | 74 +++++++-- server/router/api/v1/idp_service.go | 1 + .../CreateIdentityProviderDialog.tsx | 4 + web/src/types/proto/api/v1/idp_service.ts | 142 ++++++++++++++--- 7 files changed, 388 insertions(+), 98 deletions(-) diff --git a/proto/api/v1/idp_service.proto b/proto/api/v1/idp_service.proto index 7c7d47e5b..5d2a273d3 100644 --- a/proto/api/v1/idp_service.proto +++ b/proto/api/v1/idp_service.proto @@ -4,6 +4,8 @@ package memos.api.v1; import "google/api/annotations.proto"; import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; @@ -14,18 +16,22 @@ service IdentityProviderService { rpc ListIdentityProviders(ListIdentityProvidersRequest) returns (ListIdentityProvidersResponse) { option (google.api.http) = {get: "/api/v1/identityProviders"}; } + // GetIdentityProvider gets an identity provider. rpc GetIdentityProvider(GetIdentityProviderRequest) returns (IdentityProvider) { option (google.api.http) = {get: "/api/v1/{name=identityProviders/*}"}; option (google.api.method_signature) = "name"; } + // CreateIdentityProvider creates an identity provider. rpc CreateIdentityProvider(CreateIdentityProviderRequest) returns (IdentityProvider) { option (google.api.http) = { post: "/api/v1/identityProviders" body: "identity_provider" }; + option (google.api.method_signature) = "identity_provider"; } + // UpdateIdentityProvider updates an identity provider. rpc UpdateIdentityProvider(UpdateIdentityProviderRequest) returns (IdentityProvider) { option (google.api.http) = { @@ -34,6 +40,7 @@ service IdentityProviderService { }; option (google.api.method_signature) = "identity_provider,update_mask"; } + // DeleteIdentityProvider deletes an identity provider. rpc DeleteIdentityProvider(DeleteIdentityProviderRequest) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/api/v1/{name=identityProviders/*}"}; @@ -42,21 +49,37 @@ service IdentityProviderService { } message IdentityProvider { - // The name of the identityProvider. - // Format: identityProviders/{id}, id is the system generated auto-incremented id. - string name = 1; + option (google.api.resource) = { + type: "memos.api.v1/IdentityProvider" + pattern: "identityProviders/{idp}" + name_field: "name" + singular: "identityProvider" + plural: "identityProviders" + }; + + // The resource name of the identity provider. + // Format: identityProviders/{idp} + string name = 1 [(google.api.field_behavior) = IDENTIFIER]; + + // Output only. The system generated unique identifier. + string uid = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Required. The type of the identity provider. + Type type = 3 [(google.api.field_behavior) = REQUIRED]; + + // Required. The display title of the identity provider. + string title = 4 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Filter applied to user identifiers. + string identifier_filter = 5 [(google.api.field_behavior) = OPTIONAL]; + + // Required. Configuration for the identity provider. + IdentityProviderConfig config = 6 [(google.api.field_behavior) = REQUIRED]; enum Type { TYPE_UNSPECIFIED = 0; OAUTH2 = 1; } - Type type = 2; - - string title = 3; - - string identifier_filter = 4; - - IdentityProviderConfig config = 5; } message IdentityProviderConfig { @@ -82,32 +105,54 @@ message OAuth2Config { FieldMapping field_mapping = 7; } -message ListIdentityProvidersRequest {} +message ListIdentityProvidersRequest { + // Optional. The maximum number of identity providers to return. + int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. A page token for pagination. + string page_token = 2 [(google.api.field_behavior) = OPTIONAL]; +} message ListIdentityProvidersResponse { + // The list of identity providers. repeated IdentityProvider identity_providers = 1; + + // A token for the next page of results. + string next_page_token = 2; } message GetIdentityProviderRequest { - // The name of the identityProvider to get. - string name = 1; + // Required. The resource name of the identity provider to get. + // Format: identityProviders/{idp} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"} + ]; } message CreateIdentityProviderRequest { - // The identityProvider to create. - IdentityProvider identity_provider = 1; + // Required. The identity provider to create. + IdentityProvider identity_provider = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The ID to use for the identity provider, which will become the final component of the resource name. + // If not provided, the system will generate one. + string identity_provider_id = 2 [(google.api.field_behavior) = OPTIONAL]; } message UpdateIdentityProviderRequest { - // The identityProvider to update. - IdentityProvider identity_provider = 1; + // Required. The identity provider to update. + IdentityProvider identity_provider = 1 [(google.api.field_behavior) = REQUIRED]; - // The update mask applies to the resource. Only the top level fields of + // Required. The update mask applies to the resource. Only the top level fields of // IdentityProvider are supported. - google.protobuf.FieldMask update_mask = 2; + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; } message DeleteIdentityProviderRequest { - // The name of the identityProvider to delete. - string name = 1; + // Required. The resource name of the identity provider to delete. + // Format: identityProviders/{idp} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"} + ]; } diff --git a/proto/gen/api/v1/idp_service.pb.go b/proto/gen/api/v1/idp_service.pb.go index 6a6f94db5..45bfae840 100644 --- a/proto/gen/api/v1/idp_service.pb.go +++ b/proto/gen/api/v1/idp_service.pb.go @@ -72,15 +72,21 @@ func (IdentityProvider_Type) EnumDescriptor() ([]byte, []int) { type IdentityProvider struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the identityProvider. - // Format: identityProviders/{id}, id is the system generated auto-incremented id. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type IdentityProvider_Type `protobuf:"varint,2,opt,name=type,proto3,enum=memos.api.v1.IdentityProvider_Type" json:"type,omitempty"` - Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` - IdentifierFilter string `protobuf:"bytes,4,opt,name=identifier_filter,json=identifierFilter,proto3" json:"identifier_filter,omitempty"` - Config *IdentityProviderConfig `protobuf:"bytes,5,opt,name=config,proto3" json:"config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // The resource name of the identity provider. + // Format: identityProviders/{idp} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Output only. The system generated unique identifier. + Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"` + // Required. The type of the identity provider. + Type IdentityProvider_Type `protobuf:"varint,3,opt,name=type,proto3,enum=memos.api.v1.IdentityProvider_Type" json:"type,omitempty"` + // Required. The display title of the identity provider. + Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"` + // Optional. Filter applied to user identifiers. + IdentifierFilter string `protobuf:"bytes,5,opt,name=identifier_filter,json=identifierFilter,proto3" json:"identifier_filter,omitempty"` + // Required. Configuration for the identity provider. + Config *IdentityProviderConfig `protobuf:"bytes,6,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *IdentityProvider) Reset() { @@ -120,6 +126,13 @@ func (x *IdentityProvider) GetName() string { return "" } +func (x *IdentityProvider) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + func (x *IdentityProvider) GetType() IdentityProvider_Type { if x != nil { return x.Type @@ -375,7 +388,11 @@ func (x *OAuth2Config) GetFieldMapping() *FieldMapping { } type ListIdentityProvidersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` + state protoimpl.MessageState `protogen:"open.v1"` + // Optional. The maximum number of identity providers to return. + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // Optional. A page token for pagination. + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -410,11 +427,28 @@ func (*ListIdentityProvidersRequest) Descriptor() ([]byte, []int) { return file_api_v1_idp_service_proto_rawDescGZIP(), []int{4} } +func (x *ListIdentityProvidersRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListIdentityProvidersRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + type ListIdentityProvidersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - IdentityProviders []*IdentityProvider `protobuf:"bytes,1,rep,name=identity_providers,json=identityProviders,proto3" json:"identity_providers,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + // The list of identity providers. + IdentityProviders []*IdentityProvider `protobuf:"bytes,1,rep,name=identity_providers,json=identityProviders,proto3" json:"identity_providers,omitempty"` + // A token for the next page of results. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ListIdentityProvidersResponse) Reset() { @@ -454,9 +488,17 @@ func (x *ListIdentityProvidersResponse) GetIdentityProviders() []*IdentityProvid return nil } +func (x *ListIdentityProvidersResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + type GetIdentityProviderRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the identityProvider to get. + // Required. The resource name of the identity provider to get. + // Format: identityProviders/{idp} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -501,10 +543,13 @@ func (x *GetIdentityProviderRequest) GetName() string { type CreateIdentityProviderRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The identityProvider to create. + // Required. The identity provider to create. IdentityProvider *IdentityProvider `protobuf:"bytes,1,opt,name=identity_provider,json=identityProvider,proto3" json:"identity_provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Optional. The ID to use for the identity provider, which will become the final component of the resource name. + // If not provided, the system will generate one. + IdentityProviderId string `protobuf:"bytes,2,opt,name=identity_provider_id,json=identityProviderId,proto3" json:"identity_provider_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CreateIdentityProviderRequest) Reset() { @@ -544,11 +589,18 @@ func (x *CreateIdentityProviderRequest) GetIdentityProvider() *IdentityProvider return nil } +func (x *CreateIdentityProviderRequest) GetIdentityProviderId() string { + if x != nil { + return x.IdentityProviderId + } + return "" +} + type UpdateIdentityProviderRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The identityProvider to update. + // Required. The identity provider to update. IdentityProvider *IdentityProvider `protobuf:"bytes,1,opt,name=identity_provider,json=identityProvider,proto3" json:"identity_provider,omitempty"` - // The update mask applies to the resource. Only the top level fields of + // Required. The update mask applies to the resource. Only the top level fields of // IdentityProvider are supported. UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` unknownFields protoimpl.UnknownFields @@ -601,7 +653,8 @@ func (x *UpdateIdentityProviderRequest) GetUpdateMask() *fieldmaskpb.FieldMask { type DeleteIdentityProviderRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The name of the identityProvider to delete. + // Required. The resource name of the identity provider to delete. + // Format: identityProviders/{idp} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -648,17 +701,19 @@ var File_api_v1_idp_service_proto protoreflect.FileDescriptor const file_api_v1_idp_service_proto_rawDesc = "" + "\n" + - "\x18api/v1/idp_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\x8a\x02\n" + - "\x10IdentityProvider\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x127\n" + - "\x04type\x18\x02 \x01(\x0e2#.memos.api.v1.IdentityProvider.TypeR\x04type\x12\x14\n" + - "\x05title\x18\x03 \x01(\tR\x05title\x12+\n" + - "\x11identifier_filter\x18\x04 \x01(\tR\x10identifierFilter\x12<\n" + - "\x06config\x18\x05 \x01(\v2$.memos.api.v1.IdentityProviderConfigR\x06config\"(\n" + + "\x18api/v1/idp_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\xa2\x03\n" + + "\x10IdentityProvider\x12\x17\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12\x15\n" + + "\x03uid\x18\x02 \x01(\tB\x03\xe0A\x03R\x03uid\x12<\n" + + "\x04type\x18\x03 \x01(\x0e2#.memos.api.v1.IdentityProvider.TypeB\x03\xe0A\x02R\x04type\x12\x19\n" + + "\x05title\x18\x04 \x01(\tB\x03\xe0A\x02R\x05title\x120\n" + + "\x11identifier_filter\x18\x05 \x01(\tB\x03\xe0A\x01R\x10identifierFilter\x12A\n" + + "\x06config\x18\x06 \x01(\v2$.memos.api.v1.IdentityProviderConfigB\x03\xe0A\x02R\x06config\"(\n" + "\x04Type\x12\x14\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\n" + "\n" + - "\x06OAUTH2\x10\x01\"e\n" + + "\x06OAUTH2\x10\x01:f\xeaAc\n" + + "\x1dmemos.api.v1/IdentityProvider\x12\x17identityProviders/{idp}\x1a\x04name*\x11identityProviders2\x10identityProvider\"e\n" + "\x16IdentityProviderConfig\x12A\n" + "\roauth2_config\x18\x01 \x01(\v2\x1a.memos.api.v1.OAuth2ConfigH\x00R\foauth2ConfigB\b\n" + "\x06config\"\x86\x01\n" + @@ -677,24 +732,31 @@ const file_api_v1_idp_service_proto_rawDesc = "" + "\ttoken_url\x18\x04 \x01(\tR\btokenUrl\x12\"\n" + "\ruser_info_url\x18\x05 \x01(\tR\vuserInfoUrl\x12\x16\n" + "\x06scopes\x18\x06 \x03(\tR\x06scopes\x12?\n" + - "\rfield_mapping\x18\a \x01(\v2\x1a.memos.api.v1.FieldMappingR\ffieldMapping\"\x1e\n" + - "\x1cListIdentityProvidersRequest\"n\n" + + "\rfield_mapping\x18\a \x01(\v2\x1a.memos.api.v1.FieldMappingR\ffieldMapping\"d\n" + + "\x1cListIdentityProvidersRequest\x12 \n" + + "\tpage_size\x18\x01 \x01(\x05B\x03\xe0A\x01R\bpageSize\x12\"\n" + + "\n" + + "page_token\x18\x02 \x01(\tB\x03\xe0A\x01R\tpageToken\"\x96\x01\n" + "\x1dListIdentityProvidersResponse\x12M\n" + - "\x12identity_providers\x18\x01 \x03(\v2\x1e.memos.api.v1.IdentityProviderR\x11identityProviders\"0\n" + - "\x1aGetIdentityProviderRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"l\n" + - "\x1dCreateIdentityProviderRequest\x12K\n" + - "\x11identity_provider\x18\x01 \x01(\v2\x1e.memos.api.v1.IdentityProviderR\x10identityProvider\"\xa9\x01\n" + - "\x1dUpdateIdentityProviderRequest\x12K\n" + - "\x11identity_provider\x18\x01 \x01(\v2\x1e.memos.api.v1.IdentityProviderR\x10identityProvider\x12;\n" + - "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + - "updateMask\"3\n" + - "\x1dDeleteIdentityProviderRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name2\xce\x06\n" + + "\x12identity_providers\x18\x01 \x03(\v2\x1e.memos.api.v1.IdentityProviderR\x11identityProviders\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"W\n" + + "\x1aGetIdentityProviderRequest\x129\n" + + "\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" + + "\x1dmemos.api.v1/IdentityProviderR\x04name\"\xa8\x01\n" + + "\x1dCreateIdentityProviderRequest\x12P\n" + + "\x11identity_provider\x18\x01 \x01(\v2\x1e.memos.api.v1.IdentityProviderB\x03\xe0A\x02R\x10identityProvider\x125\n" + + "\x14identity_provider_id\x18\x02 \x01(\tB\x03\xe0A\x01R\x12identityProviderId\"\xb3\x01\n" + + "\x1dUpdateIdentityProviderRequest\x12P\n" + + "\x11identity_provider\x18\x01 \x01(\v2\x1e.memos.api.v1.IdentityProviderB\x03\xe0A\x02R\x10identityProvider\x12@\n" + + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x02R\n" + + "updateMask\"Z\n" + + "\x1dDeleteIdentityProviderRequest\x129\n" + + "\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" + + "\x1dmemos.api.v1/IdentityProviderR\x04name2\xe2\x06\n" + "\x17IdentityProviderService\x12\x93\x01\n" + "\x15ListIdentityProviders\x12*.memos.api.v1.ListIdentityProvidersRequest\x1a+.memos.api.v1.ListIdentityProvidersResponse\"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/api/v1/identityProviders\x12\x92\x01\n" + - "\x13GetIdentityProvider\x12(.memos.api.v1.GetIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{name=identityProviders/*}\x12\x9b\x01\n" + - "\x16CreateIdentityProvider\x12+.memos.api.v1.CreateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"4\x82\xd3\xe4\x93\x02.:\x11identity_provider\"\x19/api/v1/identityProviders\x12\xd6\x01\n" + + "\x13GetIdentityProvider\x12(.memos.api.v1.GetIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{name=identityProviders/*}\x12\xaf\x01\n" + + "\x16CreateIdentityProvider\x12+.memos.api.v1.CreateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"H\xdaA\x11identity_provider\x82\xd3\xe4\x93\x02.:\x11identity_provider\"\x19/api/v1/identityProviders\x12\xd6\x01\n" + "\x16UpdateIdentityProvider\x12+.memos.api.v1.UpdateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"o\xdaA\x1didentity_provider,update_mask\x82\xd3\xe4\x93\x02I:\x11identity_provider24/api/v1/{identity_provider.name=identityProviders/*}\x12\x90\x01\n" + "\x16DeleteIdentityProvider\x12+.memos.api.v1.DeleteIdentityProviderRequest\x1a\x16.google.protobuf.Empty\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$*\"/api/v1/{name=identityProviders/*}B\xa7\x01\n" + "\x10com.memos.api.v1B\x0fIdpServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" diff --git a/proto/gen/api/v1/idp_service.pb.gw.go b/proto/gen/api/v1/idp_service.pb.gw.go index 35a41c586..c303e130b 100644 --- a/proto/gen/api/v1/idp_service.pb.gw.go +++ b/proto/gen/api/v1/idp_service.pb.gw.go @@ -35,12 +35,20 @@ var ( _ = metadata.Join ) +var filter_IdentityProviderService_ListIdentityProviders_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + func request_IdentityProviderService_ListIdentityProviders_0(ctx context.Context, marshaler runtime.Marshaler, client IdentityProviderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq ListIdentityProvidersRequest metadata runtime.ServerMetadata ) io.Copy(io.Discard, req.Body) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_IdentityProviderService_ListIdentityProviders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := client.ListIdentityProviders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } @@ -50,6 +58,12 @@ func local_request_IdentityProviderService_ListIdentityProviders_0(ctx context.C protoReq ListIdentityProvidersRequest metadata runtime.ServerMetadata ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_IdentityProviderService_ListIdentityProviders_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := server.ListIdentityProviders(ctx, &protoReq) return msg, metadata, err } @@ -91,6 +105,8 @@ func local_request_IdentityProviderService_GetIdentityProvider_0(ctx context.Con return msg, metadata, err } +var filter_IdentityProviderService_CreateIdentityProvider_0 = &utilities.DoubleArray{Encoding: map[string]int{"identity_provider": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + func request_IdentityProviderService_CreateIdentityProvider_0(ctx context.Context, marshaler runtime.Marshaler, client IdentityProviderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq CreateIdentityProviderRequest @@ -99,6 +115,12 @@ func request_IdentityProviderService_CreateIdentityProvider_0(ctx context.Contex if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.IdentityProvider); err != nil && !errors.Is(err, io.EOF) { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_IdentityProviderService_CreateIdentityProvider_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := client.CreateIdentityProvider(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } @@ -111,6 +133,12 @@ func local_request_IdentityProviderService_CreateIdentityProvider_0(ctx context. if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.IdentityProvider); err != nil && !errors.Is(err, io.EOF) { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_IdentityProviderService_CreateIdentityProvider_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } msg, err := server.CreateIdentityProvider(ctx, &protoReq) return msg, metadata, err } diff --git a/proto/gen/apidocs.swagger.yaml b/proto/gen/apidocs.swagger.yaml index 6ec747f8c..de043524d 100644 --- a/proto/gen/apidocs.swagger.yaml +++ b/proto/gen/apidocs.swagger.yaml @@ -136,6 +136,18 @@ paths: description: An unexpected error response. schema: $ref: '#/definitions/googlerpcStatus' + parameters: + - name: pageSize + description: Optional. The maximum number of identity providers to return. + in: query + required: false + type: integer + format: int32 + - name: pageToken + description: Optional. A page token for pagination. + in: query + required: false + type: string tags: - IdentityProviderService post: @@ -152,11 +164,20 @@ paths: $ref: '#/definitions/googlerpcStatus' parameters: - name: identityProvider - description: The identityProvider to create. + description: Required. The identity provider to create. in: body required: true schema: $ref: '#/definitions/apiv1IdentityProvider' + required: + - identityProvider + - name: identityProviderId + description: |- + Optional. The ID to use for the identity provider, which will become the final component of the resource name. + If not provided, the system will generate one. + in: query + required: false + type: string tags: - IdentityProviderService /api/v1/inboxes: @@ -722,28 +743,41 @@ paths: parameters: - name: identityProvider.name description: |- - The name of the identityProvider. - Format: identityProviders/{id}, id is the system generated auto-incremented id. + The resource name of the identity provider. + Format: identityProviders/{idp} in: path required: true type: string pattern: identityProviders/[^/]+ - name: identityProvider - description: The identityProvider to update. + description: Required. The identity provider to update. in: body required: true schema: type: object properties: + uid: + type: string + description: Output only. The system generated unique identifier. + readOnly: true type: $ref: '#/definitions/apiv1IdentityProviderType' + description: Required. The type of the identity provider. title: type: string + description: Required. The display title of the identity provider. identifierFilter: type: string + description: Optional. Filter applied to user identifiers. config: $ref: '#/definitions/apiv1IdentityProviderConfig' - title: The identityProvider to update. + description: Required. Configuration for the identity provider. + title: Required. The identity provider to update. + required: + - type + - title + - config + - identityProvider tags: - IdentityProviderService /api/v1/{inbox.name}: @@ -965,7 +999,9 @@ paths: $ref: '#/definitions/googlerpcStatus' parameters: - name: name_2 - description: The name of the identityProvider to get. + description: |- + Required. The resource name of the identity provider to get. + Format: identityProviders/{idp} in: path required: true type: string @@ -987,7 +1023,9 @@ paths: $ref: '#/definitions/googlerpcStatus' parameters: - name: name_2 - description: The name of the identityProvider to delete. + description: |- + Required. The resource name of the identity provider to delete. + Format: identityProviders/{idp} in: path required: true type: string @@ -2394,17 +2432,29 @@ definitions: properties: name: type: string - description: |- - The name of the identityProvider. - Format: identityProviders/{id}, id is the system generated auto-incremented id. + title: |- + The resource name of the identity provider. + Format: identityProviders/{idp} + uid: + type: string + description: Output only. The system generated unique identifier. + readOnly: true type: $ref: '#/definitions/apiv1IdentityProviderType' + description: Required. The type of the identity provider. title: type: string + description: Required. The display title of the identity provider. identifierFilter: type: string + description: Optional. Filter applied to user identifiers. config: $ref: '#/definitions/apiv1IdentityProviderConfig' + description: Required. Configuration for the identity provider. + required: + - type + - title + - config apiv1IdentityProviderConfig: type: object properties: @@ -3036,6 +3086,10 @@ definitions: items: type: object $ref: '#/definitions/apiv1IdentityProvider' + description: The list of identity providers. + nextPageToken: + type: string + description: A token for the next page of results. v1ListInboxesResponse: type: object properties: diff --git a/server/router/api/v1/idp_service.go b/server/router/api/v1/idp_service.go index fb52513e2..22024cc1c 100644 --- a/server/router/api/v1/idp_service.go +++ b/server/router/api/v1/idp_service.go @@ -106,6 +106,7 @@ func (s *APIV1Service) DeleteIdentityProvider(ctx context.Context, request *v1pb func convertIdentityProviderFromStore(identityProvider *storepb.IdentityProvider) *v1pb.IdentityProvider { temp := &v1pb.IdentityProvider{ Name: fmt.Sprintf("%s%d", IdentityProviderNamePrefix, identityProvider.Id), + Uid: fmt.Sprintf("%d", identityProvider.Id), Title: identityProvider.Name, IdentifierFilter: identityProvider.IdentifierFilter, Type: v1pb.IdentityProvider_Type(v1pb.IdentityProvider_Type_value[identityProvider.Type.String()]), diff --git a/web/src/components/CreateIdentityProviderDialog.tsx b/web/src/components/CreateIdentityProviderDialog.tsx index 67aed6802..ee8d2d8fd 100644 --- a/web/src/components/CreateIdentityProviderDialog.tsx +++ b/web/src/components/CreateIdentityProviderDialog.tsx @@ -12,6 +12,7 @@ import { generateDialog } from "./Dialog"; const templateList: IdentityProvider[] = [ { name: "", + uid: "", title: "GitHub", type: IdentityProvider_Type.OAUTH2, identifierFilter: "", @@ -33,6 +34,7 @@ const templateList: IdentityProvider[] = [ }, { name: "", + uid: "", title: "GitLab", type: IdentityProvider_Type.OAUTH2, identifierFilter: "", @@ -54,6 +56,7 @@ const templateList: IdentityProvider[] = [ }, { name: "", + uid: "", title: "Google", type: IdentityProvider_Type.OAUTH2, identifierFilter: "", @@ -75,6 +78,7 @@ const templateList: IdentityProvider[] = [ }, { name: "", + uid: "", title: "Custom", type: IdentityProvider_Type.OAUTH2, identifierFilter: "", diff --git a/web/src/types/proto/api/v1/idp_service.ts b/web/src/types/proto/api/v1/idp_service.ts index bb04f1781..11710d3fa 100644 --- a/web/src/types/proto/api/v1/idp_service.ts +++ b/web/src/types/proto/api/v1/idp_service.ts @@ -13,13 +13,19 @@ export const protobufPackage = "memos.api.v1"; export interface IdentityProvider { /** - * The name of the identityProvider. - * Format: identityProviders/{id}, id is the system generated auto-incremented id. + * The resource name of the identity provider. + * Format: identityProviders/{idp} */ name: string; + /** Output only. The system generated unique identifier. */ + uid: string; + /** Required. The type of the identity provider. */ type: IdentityProvider_Type; + /** Required. The display title of the identity provider. */ title: string; + /** Optional. Filter applied to user identifiers. */ identifierFilter: string; + /** Required. Configuration for the identity provider. */ config?: IdentityProviderConfig | undefined; } @@ -78,41 +84,68 @@ export interface OAuth2Config { } export interface ListIdentityProvidersRequest { + /** Optional. The maximum number of identity providers to return. */ + pageSize: number; + /** Optional. A page token for pagination. */ + pageToken: string; } export interface ListIdentityProvidersResponse { + /** The list of identity providers. */ identityProviders: IdentityProvider[]; + /** A token for the next page of results. */ + nextPageToken: string; } export interface GetIdentityProviderRequest { - /** The name of the identityProvider to get. */ + /** + * Required. The resource name of the identity provider to get. + * Format: identityProviders/{idp} + */ name: string; } export interface CreateIdentityProviderRequest { - /** The identityProvider to create. */ - identityProvider?: IdentityProvider | undefined; + /** Required. The identity provider to create. */ + identityProvider?: + | IdentityProvider + | undefined; + /** + * Optional. The ID to use for the identity provider, which will become the final component of the resource name. + * If not provided, the system will generate one. + */ + identityProviderId: string; } export interface UpdateIdentityProviderRequest { - /** The identityProvider to update. */ + /** Required. The identity provider to update. */ identityProvider?: | IdentityProvider | undefined; /** - * The update mask applies to the resource. Only the top level fields of + * Required. The update mask applies to the resource. Only the top level fields of * IdentityProvider are supported. */ updateMask?: string[] | undefined; } export interface DeleteIdentityProviderRequest { - /** The name of the identityProvider to delete. */ + /** + * Required. The resource name of the identity provider to delete. + * Format: identityProviders/{idp} + */ name: string; } function createBaseIdentityProvider(): IdentityProvider { - return { name: "", type: IdentityProvider_Type.TYPE_UNSPECIFIED, title: "", identifierFilter: "", config: undefined }; + return { + name: "", + uid: "", + type: IdentityProvider_Type.TYPE_UNSPECIFIED, + title: "", + identifierFilter: "", + config: undefined, + }; } export const IdentityProvider: MessageFns = { @@ -120,17 +153,20 @@ export const IdentityProvider: MessageFns = { if (message.name !== "") { writer.uint32(10).string(message.name); } + if (message.uid !== "") { + writer.uint32(18).string(message.uid); + } if (message.type !== IdentityProvider_Type.TYPE_UNSPECIFIED) { - writer.uint32(16).int32(identityProvider_TypeToNumber(message.type)); + writer.uint32(24).int32(identityProvider_TypeToNumber(message.type)); } if (message.title !== "") { - writer.uint32(26).string(message.title); + writer.uint32(34).string(message.title); } if (message.identifierFilter !== "") { - writer.uint32(34).string(message.identifierFilter); + writer.uint32(42).string(message.identifierFilter); } if (message.config !== undefined) { - IdentityProviderConfig.encode(message.config, writer.uint32(42).fork()).join(); + IdentityProviderConfig.encode(message.config, writer.uint32(50).fork()).join(); } return writer; }, @@ -151,19 +187,19 @@ export const IdentityProvider: MessageFns = { continue; } case 2: { - if (tag !== 16) { + if (tag !== 18) { break; } - message.type = identityProvider_TypeFromJSON(reader.int32()); + message.uid = reader.string(); continue; } case 3: { - if (tag !== 26) { + if (tag !== 24) { break; } - message.title = reader.string(); + message.type = identityProvider_TypeFromJSON(reader.int32()); continue; } case 4: { @@ -171,7 +207,7 @@ export const IdentityProvider: MessageFns = { break; } - message.identifierFilter = reader.string(); + message.title = reader.string(); continue; } case 5: { @@ -179,6 +215,14 @@ export const IdentityProvider: MessageFns = { break; } + message.identifierFilter = reader.string(); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + message.config = IdentityProviderConfig.decode(reader, reader.uint32()); continue; } @@ -197,6 +241,7 @@ export const IdentityProvider: MessageFns = { fromPartial(object: DeepPartial): IdentityProvider { const message = createBaseIdentityProvider(); message.name = object.name ?? ""; + message.uid = object.uid ?? ""; message.type = object.type ?? IdentityProvider_Type.TYPE_UNSPECIFIED; message.title = object.title ?? ""; message.identifierFilter = object.identifierFilter ?? ""; @@ -466,11 +511,17 @@ export const OAuth2Config: MessageFns = { }; function createBaseListIdentityProvidersRequest(): ListIdentityProvidersRequest { - return {}; + return { pageSize: 0, pageToken: "" }; } export const ListIdentityProvidersRequest: MessageFns = { - encode(_: ListIdentityProvidersRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + encode(message: ListIdentityProvidersRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.pageSize !== 0) { + writer.uint32(8).int32(message.pageSize); + } + if (message.pageToken !== "") { + writer.uint32(18).string(message.pageToken); + } return writer; }, @@ -481,6 +532,22 @@ export const ListIdentityProvidersRequest: MessageFns>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.pageSize = reader.int32(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.pageToken = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -493,14 +560,16 @@ export const ListIdentityProvidersRequest: MessageFns): ListIdentityProvidersRequest { return ListIdentityProvidersRequest.fromPartial(base ?? {}); }, - fromPartial(_: DeepPartial): ListIdentityProvidersRequest { + fromPartial(object: DeepPartial): ListIdentityProvidersRequest { const message = createBaseListIdentityProvidersRequest(); + message.pageSize = object.pageSize ?? 0; + message.pageToken = object.pageToken ?? ""; return message; }, }; function createBaseListIdentityProvidersResponse(): ListIdentityProvidersResponse { - return { identityProviders: [] }; + return { identityProviders: [], nextPageToken: "" }; } export const ListIdentityProvidersResponse: MessageFns = { @@ -508,6 +577,9 @@ export const ListIdentityProvidersResponse: MessageFns): ListIdentityProvidersResponse { const message = createBaseListIdentityProvidersResponse(); message.identityProviders = object.identityProviders?.map((e) => IdentityProvider.fromPartial(e)) || []; + message.nextPageToken = object.nextPageToken ?? ""; return message; }, }; @@ -592,7 +673,7 @@ export const GetIdentityProviderRequest: MessageFns }; function createBaseCreateIdentityProviderRequest(): CreateIdentityProviderRequest { - return { identityProvider: undefined }; + return { identityProvider: undefined, identityProviderId: "" }; } export const CreateIdentityProviderRequest: MessageFns = { @@ -600,6 +681,9 @@ export const CreateIdentityProviderRequest: MessageFns