Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6ee9154

Browse files
committedJun 10, 2021
feat: hashicorp-grpc plugin implementation
Signed-off-by: Jimil Desai <[email protected]>
1 parent c5c4203 commit 6ee9154

File tree

10 files changed

+2728
-3
lines changed

10 files changed

+2728
-3
lines changed
 

‎go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ require (
88
github.com/hashicorp/go-hclog v0.14.1
99
github.com/hashicorp/go-plugin v1.4.2
1010
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
11+
google.golang.org/grpc v1.37.0
12+
google.golang.org/protobuf v1.26.0
1113
)

‎hashicorp-plugin-grpc

13.5 MB
Binary file not shown.

‎main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func main() {
2121
client := plugin.NewClient(&plugin.ClientConfig{
2222
HandshakeConfig: shared.Handshake,
2323
Plugins: shared.PluginMap,
24-
Cmd: exec.Command("./hashicorp-plugin"),
24+
Cmd: exec.Command("./hashicorp-plugin-grpc"),
2525
AllowedProtocols: []plugin.Protocol{
2626
plugin.ProtocolNetRPC, plugin.ProtocolGRPC},
2727
})
@@ -35,7 +35,7 @@ func main() {
3535
}
3636

3737
// Request the plugin
38-
raw, err := rpcClient.Dispense("json")
38+
raw, err := rpcClient.Dispense("json_grpc")
3939
if err != nil {
4040
fmt.Println("Error:", err.Error())
4141
os.Exit(1)

‎pkg/plugins/hashicorp/grpc/main.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"io/ioutil"
7+
"strings"
8+
9+
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
10+
"github.com/cs3org/reva/pkg/errtypes"
11+
"github.com/hashicorp/go-plugin"
12+
"github.com/jimil749/reva-plugin-benchmark/pkg/shared"
13+
)
14+
15+
// Here is a real implementation of Manager
16+
type Manager struct {
17+
users []*userpb.User
18+
}
19+
20+
func (m *Manager) OnLoad(userFile string) error {
21+
f, err := ioutil.ReadFile(userFile)
22+
if err != nil {
23+
return err
24+
}
25+
26+
users := []*userpb.User{}
27+
28+
err = json.Unmarshal(f, &users)
29+
if err != nil {
30+
return err
31+
}
32+
33+
m.users = users
34+
35+
return nil
36+
}
37+
38+
func (m *Manager) GetUser(uid *userpb.UserId) (*userpb.User, error) {
39+
for _, u := range m.users {
40+
if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) {
41+
return u, nil
42+
}
43+
}
44+
return nil, nil
45+
}
46+
47+
func (m *Manager) GetUserByClaim(claim, value string) (*userpb.User, error) {
48+
for _, u := range m.users {
49+
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
50+
return u, nil
51+
}
52+
}
53+
return nil, errtypes.NotFound(value)
54+
}
55+
56+
func extractClaim(u *userpb.User, claim string) (string, error) {
57+
switch claim {
58+
case "mail":
59+
return u.Mail, nil
60+
case "username":
61+
return u.Username, nil
62+
case "uid":
63+
if u.Opaque != nil && u.Opaque.Map != nil {
64+
if uidObj, ok := u.Opaque.Map["uid"]; ok {
65+
if uidObj.Decoder == "plain" {
66+
return string(uidObj.Value), nil
67+
}
68+
}
69+
}
70+
}
71+
return "", errors.New("json: invalid field")
72+
}
73+
74+
// TODO(jfd) search Opaque? compare sub?
75+
func userContains(u *userpb.User, query string) bool {
76+
query = strings.ToLower(query)
77+
return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) ||
78+
strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(u.Id.OpaqueId), query)
79+
}
80+
81+
func (m *Manager) FindUsers(query string) ([]*userpb.User, error) {
82+
users := []*userpb.User{}
83+
for _, u := range m.users {
84+
if userContains(u, query) {
85+
users = append(users, u)
86+
}
87+
}
88+
return users, nil
89+
}
90+
91+
func (m *Manager) GetUserGroups(uid *userpb.UserId) ([]string, error) {
92+
user, err := m.GetUser(uid)
93+
if err != nil {
94+
return nil, err
95+
}
96+
return user.Groups, nil
97+
}
98+
99+
func main() {
100+
plugin.Serve(&plugin.ServeConfig{
101+
HandshakeConfig: shared.Handshake,
102+
Plugins: map[string]plugin.Plugin{
103+
"json_grpc": &shared.JSONPluginGRPC{Impl: &Manager{}},
104+
},
105+
106+
GRPCServer: plugin.DefaultGRPCServer,
107+
})
108+
}

‎pkg/proto/manager.pb.go

+1,772
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pkg/proto/manager.proto

+350
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
syntax = "proto3";
2+
package proto;
3+
4+
option go_package="github.com/jimil749/reva-plugin-benchmark/pkg/proto/manager";
5+
6+
// A UserId represents a user.
7+
message UserId {
8+
// REQUIRED.
9+
// The identity provider for the user.
10+
string idp = 1;
11+
// REQUIRED.
12+
// the unique identifier for the user in the scope of
13+
// the identity provider.
14+
string opaque_id = 2;
15+
// REQUIRED.
16+
// The type of user.
17+
UserType type = 3;
18+
}
19+
20+
// Represents a user of the system.
21+
message User {
22+
UserId id = 1;
23+
string username = 2;
24+
string mail = 3;
25+
bool mail_verified = 4;
26+
string display_name = 5;
27+
repeated string groups = 6;
28+
Opaque opaque = 7;
29+
int64 uid_number = 8;
30+
int64 gid_number = 9;
31+
}
32+
33+
message Opaque {
34+
// REQUIRED.
35+
map<string, OpaqueEntry> map = 1;
36+
}
37+
38+
message Status {
39+
// REQUIRED.
40+
// The status code, which should be an enum value of [cs3.rpc.code][cs3.rpc.code].
41+
Code code = 1;
42+
// OPTIONAL.
43+
// A developer-facing error message, which should be in English. Any
44+
// user-facing error message should be localized and sent in the
45+
string message = 2;
46+
// OPTIONAL.
47+
// A trace added to the response for helping support to identify client problems.
48+
string trace = 3;
49+
// OPTIONAL.
50+
// A target URI as per RFC3986 to redirect requests to another location.
51+
// A Status message with CODE_REDIRECT MUST always set the target_uri.
52+
// https://golang.org/pkg/net/url/#URL provides a quick view of the format.
53+
string target_uri = 4;
54+
}
55+
56+
enum Code {
57+
// A programmer would not intentionally set the code to CODE_INVALID.
58+
// This code exists to force service implementors to set
59+
// a specific code for the API call and to not rely on defaults.
60+
//
61+
// HTTP Mapping: 500 Internal Server Error
62+
CODE_INVALID = 0;
63+
// Not an error; returned on success
64+
//
65+
// HTTP Mapping: 200 OK
66+
CODE_OK = 1;
67+
// The operation was cancelled, typically by the caller.
68+
//
69+
// HTTP Mapping: 499 Client Closed Request
70+
CODE_CANCELLED = 2;
71+
// Unknown error. For example, this error may be returned when
72+
// a `Status` value received from another address space belongs to
73+
// an error space that is not known in this address space. Also
74+
// errors raised by APIs that do not return enough error information
75+
// may be converted to this error.
76+
//
77+
// HTTP Mapping: 500 Internal Server Error
78+
CODE_UNKNOWN = 3;
79+
// The client specified an invalid argument. Note that this differs
80+
// from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments
81+
// that are problematic regardless of the state of the system
82+
// (e.g., a malformed file name).
83+
//
84+
// HTTP Mapping: 400 Bad Request
85+
CODE_INVALID_ARGUMENT = 4;
86+
// The deadline expired before the operation could complete. For operations
87+
// that change the state of the system, this error may be returned
88+
// even if the operation has completed successfully. For example, a
89+
// successful response from a server could have been delayed long
90+
// enough for the deadline to expire.
91+
//
92+
// HTTP Mapping: 504 Gateway Timeout
93+
CODE_DEADLINE_EXCEEDED = 5;
94+
// Some requested entity (e.g., file or directory) was not found.
95+
//
96+
// Note to server developers: if a request is denied for an entire class
97+
// of users, such as gradual feature rollout or undocumented whitelist,
98+
// `NOT_FOUND` may be used. If a request is denied for some users within
99+
// a class of users, such as user-based access control, `PERMISSION_DENIED`
100+
// must be used.
101+
//
102+
// HTTP Mapping: 404 Not Found
103+
CODE_NOT_FOUND = 6;
104+
// The entity that a client attempted to create (e.g., file or directory)
105+
// already exists.
106+
//
107+
// HTTP Mapping: 409 Conflict
108+
CODE_ALREADY_EXISTS = 7;
109+
// The caller does not have permission to execute the specified
110+
// operation. `PERMISSION_DENIED` must not be used for rejections
111+
// caused by exhausting some resource (use `RESOURCE_EXHAUSTED`
112+
// instead for those errors). `PERMISSION_DENIED` must not be
113+
// used if the caller can not be identified (use `UNAUTHENTICATED`
114+
// instead for those errors). This error code does not imply the
115+
// request is valid or the requested entity exists or satisfies
116+
// other pre-conditions.
117+
//
118+
// HTTP Mapping: 403 Forbidden
119+
CODE_PERMISSION_DENIED = 8;
120+
// The request does not have valid authentication credentials for the
121+
// operation.
122+
//
123+
// HTTP Mapping: 401 Unauthorized
124+
CODE_UNAUTHENTICATED = 9;
125+
// Some resource has been exhausted, perhaps a per-user quota, or
126+
// perhaps the entire file system is out of space.
127+
//
128+
// HTTP Mapping: 429 Too Many Requests
129+
CODE_RESOURCE_EXHAUSTED = 10;
130+
// The operation was rejected because the system is not in a state
131+
// required for the operation's execution. For example, the directory
132+
// to be deleted is non-empty, an rmdir operation is applied to
133+
// a non-directory, etc.
134+
//
135+
// Service implementors can use the following guidelines to decide
136+
// between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`:
137+
// (a) Use `UNAVAILABLE` if the client can retry just the failing call.
138+
// (b) Use `ABORTED` if the client should retry at a higher level
139+
// (e.g., when a client-specified test-and-set fails, indicating the
140+
// client should restart a read-modify-write sequence).
141+
// (c) Use `FAILED_PRECONDITION` if the client should not retry until
142+
// the system state has been explicitly fixed. E.g., if an "rmdir"
143+
// fails because the directory is non-empty, `FAILED_PRECONDITION`
144+
// should be returned since the client should not retry unless
145+
// the files are deleted from the directory.
146+
//
147+
// HTTP Mapping: 400 Bad Request
148+
CODE_FAILED_PRECONDITION = 11;
149+
// The operation was aborted, typically due to a concurrency issue such as
150+
// a sequencer check failure or transaction abort.
151+
//
152+
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
153+
// `ABORTED`, and `UNAVAILABLE`.
154+
//
155+
// HTTP Mapping: 409 Conflict
156+
CODE_ABORTED = 12;
157+
// The operation was attempted past the valid range. E.g., seeking or
158+
// reading past end-of-file.
159+
//
160+
// Unlike `INVALID_ARGUMENT`, this error indicates a problem that may
161+
// be fixed if the system state changes. For example, a 32-bit file
162+
// system will generate `INVALID_ARGUMENT` if asked to read at an
163+
// offset that is not in the range [0,2^32-1], but it will generate
164+
// `OUT_OF_RANGE` if asked to read from an offset past the current
165+
// file size.
166+
//
167+
// There is a fair bit of overlap between `FAILED_PRECONDITION` and
168+
// `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific
169+
// error) when it applies so that callers who are iterating through
170+
// a space can easily look for an `OUT_OF_RANGE` error to detect when
171+
// they are done.
172+
//
173+
// HTTP Mapping: 400 Bad Request
174+
CODE_OUT_OF_RANGE = 13;
175+
// The operation is not implemented or is not supported/enabled in this
176+
// service.
177+
//
178+
// HTTP Mapping: 501 Not Implemented
179+
CODE_UNIMPLEMENTED = 14;
180+
// Internal errors. This means that some invariants expected by the
181+
// underlying system have been broken. This error code is reserved
182+
// for serious errors.
183+
//
184+
// HTTP Mapping: 500 Internal Server Error
185+
CODE_INTERNAL = 15;
186+
// The service is currently unavailable. This is most likely a
187+
// transient condition, which can be corrected by retrying with
188+
// a backoff.
189+
//
190+
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
191+
// `ABORTED`, and `UNAVAILABLE`.
192+
//
193+
// HTTP Mapping: 503 Service Unavailable
194+
CODE_UNAVAILABLE = 16;
195+
// Unrecoverable data loss or corruption.
196+
//
197+
// HTTP Mapping: 500 Internal Server Error
198+
CODE_DATA_LOSS = 17;
199+
// Redirects the operation to another location.
200+
// Used in a Status reponse with a reference to the target URI.
201+
CODE_REDIRECTION = 18;
202+
//
203+
// The operation could not be performed because there is not enough
204+
// storage available. This can be because of lack of real storage
205+
// space or because of the exceeding of a quota associated to a
206+
// storage.
207+
//
208+
// HTTP Mapping: 507 Insufficient Storage
209+
CODE_INSUFFICIENT_STORAGE = 19;
210+
}
211+
212+
// OpaqueEntry represents the encoded
213+
// opaque value.
214+
message OpaqueEntry {
215+
// REQUIRED.
216+
// The decoder to use: json, xml, toml, ...
217+
// TODO(labkode): make encoder a fixed set using a enum type?
218+
string decoder = 1;
219+
// REQUIRED.
220+
// The encoded value.
221+
bytes value = 2;
222+
}
223+
224+
// The type of user.
225+
enum UserType {
226+
// The user is invalid, for example, is missing primary attributes.
227+
USER_TYPE_INVALID = 0;
228+
// A primary user.
229+
USER_TYPE_PRIMARY = 1;
230+
// A secondary user for cases with multiple identities.
231+
USER_TYPE_SECONDARY = 2;
232+
// A user catering to specific services.
233+
USER_TYPE_SERVICE = 3;
234+
// A user to be used by specific applications.
235+
USER_TYPE_APPLICATION = 4;
236+
// A guest user not affiliated to the IDP.
237+
USER_TYPE_GUEST = 5;
238+
// A federated user provided by external IDPs.
239+
USER_TYPE_FEDERATED = 6;
240+
// A lightweight user account without access to various major functionalities.
241+
USER_TYPE_LIGHTWEIGHT = 7;
242+
}
243+
244+
// Provides an API for managing users.
245+
service UserAPI {
246+
// Load the plugin
247+
rpc OnLoad(OnLoadRequest) returns (OnLoadResponse);
248+
// Gets the information about a user by the user id.
249+
rpc GetUser(GetUserRequest) returns (GetUserResponse);
250+
// Gets the information about a user based on a specified claim.
251+
rpc GetUserByClaim(GetUserByClaimRequest) returns (GetUserByClaimResponse);
252+
// Gets the groups of a user.
253+
rpc GetUserGroups(GetUserGroupsRequest) returns (GetUserGroupsResponse);
254+
// Finds users by any attribute of the user.
255+
// TODO(labkode): to define the filters that make more sense.
256+
rpc FindUsers(FindUsersRequest) returns (FindUsersResponse);
257+
}
258+
259+
message OnLoadRequest {
260+
string UserFile = 1;
261+
}
262+
263+
message OnLoadResponse{}
264+
265+
message GetUserRequest {
266+
// OPTIONAL.
267+
// Opaque information.
268+
Opaque opaque = 1;
269+
// REQUIRED.
270+
// The id of the user.
271+
UserId user_id = 2;
272+
}
273+
274+
message GetUserResponse {
275+
// REQUIRED.
276+
// The response status.
277+
Status status = 1;
278+
// OPTIONAL.
279+
// Opaque information.
280+
Opaque opaque = 2;
281+
// REQUIRED.
282+
// The user information.
283+
User user = 3;
284+
}
285+
286+
message GetUserByClaimRequest {
287+
// OPTIONAL.
288+
// Opaque information.
289+
Opaque opaque = 1;
290+
// REQUIRED.
291+
// The claim on the basis of which users will be filtered.
292+
string claim = 2;
293+
// REQUIRED.
294+
// The value of the claim to find the specific user.
295+
string value = 3;
296+
}
297+
298+
message GetUserByClaimResponse {
299+
// REQUIRED.
300+
// The response status.
301+
Status status = 1;
302+
// OPTIONAL.
303+
// Opaque information.
304+
Opaque opaque = 2;
305+
// REQUIRED.
306+
// The user information.
307+
User user = 3;
308+
}
309+
310+
message GetUserGroupsRequest {
311+
// OPTIONAL.
312+
// Opaque information.
313+
Opaque opaque = 1;
314+
// REQUIRED.
315+
// The id of the user.
316+
UserId user_id = 2;
317+
}
318+
319+
message GetUserGroupsResponse {
320+
// REQUIRED.
321+
// The response status.
322+
Status status = 1;
323+
// OPTIONAL.
324+
// Opaque information.
325+
Opaque opaque = 2;
326+
// REQUIRED.
327+
// The groups for the user.
328+
repeated string groups = 3;
329+
}
330+
331+
message FindUsersRequest {
332+
// OPTIONAL.
333+
// Opaque information.
334+
Opaque opaque = 1;
335+
// REQUIRED. TODO(labkode): create proper filters for most common searches.
336+
// The filter to apply.
337+
string filter = 2;
338+
}
339+
340+
message FindUsersResponse {
341+
// REQUIRED.
342+
// The response status.
343+
Status status = 1;
344+
// OPTIONAL.
345+
// Opaque information.
346+
Opaque opaque = 2;
347+
// REQUIRED.
348+
// The users matching the specified filter.
349+
repeated User users = 3;
350+
}

‎pkg/proto/manager_grpc.pb.go

+257
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pkg/shared/grpc.go

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package shared
2+
3+
import (
4+
"context"
5+
6+
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
7+
proto "github.com/jimil749/reva-plugin-benchmark/pkg/proto"
8+
)
9+
10+
type GRPCClient struct{ client proto.UserAPIClient }
11+
12+
func (m *GRPCClient) OnLoad(userFile string) error {
13+
_, err := m.client.OnLoad(context.Background(), &proto.OnLoadRequest{
14+
UserFile: userFile,
15+
})
16+
return err
17+
}
18+
19+
func (m *GRPCClient) GetUser(uid *userpb.UserId) (*userpb.User, error) {
20+
resp, err := m.client.GetUser(context.Background(), &proto.GetUserRequest{
21+
UserId: &proto.UserId{
22+
Idp: uid.Idp,
23+
OpaqueId: uid.OpaqueId,
24+
Type: proto.UserType(uid.Type),
25+
},
26+
})
27+
if err != nil {
28+
return nil, err
29+
}
30+
// unmarshal the manager user to userpb.User
31+
user := UnmarshalUser(resp.User)
32+
return user, nil
33+
}
34+
35+
func (m *GRPCClient) GetUserByClaim(claim, value string) (*userpb.User, error) {
36+
resp, err := m.client.GetUserByClaim(context.Background(), &proto.GetUserByClaimRequest{
37+
Value: value,
38+
Claim: claim,
39+
})
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
user := UnmarshalUser(resp.User)
45+
return user, nil
46+
}
47+
48+
func (m *GRPCClient) GetUserGroups(uid *userpb.UserId) ([]string, error) {
49+
resp, err := m.client.GetUserGroups(context.Background(), &proto.GetUserGroupsRequest{
50+
UserId: &proto.UserId{
51+
Idp: uid.Idp,
52+
OpaqueId: uid.OpaqueId,
53+
Type: proto.UserType(uid.Type),
54+
},
55+
})
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
return resp.Groups, nil
61+
}
62+
63+
func (m *GRPCClient) FindUsers(filter string) ([]*userpb.User, error) {
64+
resp, err := m.client.FindUsers(context.Background(), &proto.FindUsersRequest{
65+
Filter: filter,
66+
})
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
var users []*userpb.User
72+
for _, user := range resp.Users {
73+
users = append(users, UnmarshalUser(user))
74+
}
75+
return users, nil
76+
}
77+
78+
// gRPC server that gRPC client talks to.
79+
type GRPCServer struct {
80+
Impl Manager
81+
proto.UnimplementedUserAPIServer
82+
}
83+
84+
func (m *GRPCServer) OnLoad(ctx context.Context, req *proto.OnLoadRequest) (*proto.OnLoadResponse, error) {
85+
return &proto.OnLoadResponse{}, m.Impl.OnLoad(req.UserFile)
86+
}
87+
88+
func (m *GRPCServer) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) {
89+
userpb, err := m.Impl.GetUser(UnmarshalUserId(req.UserId))
90+
user := MarshalUser(userpb)
91+
return &proto.GetUserResponse{User: user}, err
92+
}
93+
94+
func (m *GRPCServer) GetUserByClaim(ctx context.Context, req *proto.GetUserByClaimRequest) (*proto.GetUserByClaimResponse, error) {
95+
userpb, err := m.Impl.GetUserByClaim(req.Claim, req.Value)
96+
user := MarshalUser(userpb)
97+
return &proto.GetUserByClaimResponse{User: user}, err
98+
}
99+
100+
func (m *GRPCServer) GetUserGroups(ctx context.Context, req *proto.GetUserGroupsRequest) (*proto.GetUserGroupsResponse, error) {
101+
groups, err := m.Impl.GetUserGroups(UnmarshalUserId(req.UserId))
102+
return &proto.GetUserGroupsResponse{Groups: groups}, err
103+
}
104+
105+
func (m *GRPCServer) FindUsers(ctx context.Context, req *proto.FindUsersRequest) (*proto.FindUsersResponse, error) {
106+
userspb, err := m.Impl.FindUsers(req.Filter)
107+
var users []*proto.User
108+
for _, user := range userspb {
109+
users = append(users, MarshalUser(user))
110+
}
111+
return &proto.FindUsersResponse{Users: users}, err
112+
}
113+
114+
// UnmarshalUserId "unmarshals" the proto.UserId to *userpb.UserId
115+
func UnmarshalUserId(uid *proto.UserId) *userpb.UserId {
116+
return &userpb.UserId{
117+
Idp: uid.Idp,
118+
OpaqueId: uid.OpaqueId,
119+
Type: userpb.UserType(uid.Type),
120+
}
121+
}
122+
123+
// UnmarshalUser "unmarshals" the proto.User type to *userpb.User
124+
func UnmarshalUser(user *proto.User) *userpb.User {
125+
return &userpb.User{
126+
Id: &userpb.UserId{
127+
Idp: user.Id.Idp,
128+
OpaqueId: user.Id.OpaqueId,
129+
Type: userpb.UserType(user.Id.Type),
130+
},
131+
Username: user.Username,
132+
Mail: user.Mail,
133+
MailVerified: user.MailVerified,
134+
DisplayName: user.DisplayName,
135+
Groups: user.Groups,
136+
UidNumber: user.UidNumber,
137+
GidNumber: user.GidNumber,
138+
}
139+
}
140+
141+
// MarshalUser marshals userpb.User to proto.User
142+
func MarshalUser(user *userpb.User) *proto.User {
143+
return &proto.User{
144+
Id: &proto.UserId{
145+
Idp: user.Id.Idp,
146+
OpaqueId: user.Id.OpaqueId,
147+
Type: proto.UserType(user.Id.Type),
148+
},
149+
Username: user.Username,
150+
Mail: user.Mail,
151+
MailVerified: user.MailVerified,
152+
DisplayName: user.DisplayName,
153+
Groups: user.Groups,
154+
UidNumber: user.UidNumber,
155+
GidNumber: user.GidNumber,
156+
}
157+
}

‎pkg/shared/interface.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package shared
22

33
import (
4+
"context"
45
"net/rpc"
56

67
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
78
"github.com/hashicorp/go-plugin"
9+
proto "github.com/jimil749/reva-plugin-benchmark/pkg/proto"
10+
"google.golang.org/grpc"
811
)
912

1013
// Handshake is a common handshake that is shared by plugin and host.
@@ -17,7 +20,8 @@ var Handshake = plugin.HandshakeConfig{
1720

1821
// PluginMap is the map of plugins we can dispense.
1922
var PluginMap = map[string]plugin.Plugin{
20-
"json": &JSONPlugin{},
23+
"json": &JSONPlugin{},
24+
"json_grpc": &JSONPluginGRPC{},
2125
}
2226

2327
// Manager is the interface that we're exposing as a plugin. This interface is only used for plugin
@@ -45,10 +49,33 @@ func (*JSONPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, erro
4549
return &RPCClient{Client: c}, nil
4650
}
4751

52+
// ------------------------------ For Non-RPC Plugins--------------------------------------
4853
// UserManager is the interface we're exposing as a plugin for plugin systems NOT using RPC.
4954
type UserManager interface {
5055
GetUser(*userpb.UserId) (*userpb.User, error)
5156
GetUserByClaim(claim, value string) (*userpb.User, error)
5257
GetUserGroups(*userpb.UserId) ([]string, error)
5358
FindUsers(query string) ([]*userpb.User, error)
5459
}
60+
61+
type ManagerGRPC interface {
62+
OnLoad(userFile string) error
63+
GetUser(*userpb.UserId) (*userpb.User, error)
64+
GetUserByClaim(claim, value string) (*userpb.User, error)
65+
GetUserGroups(*userpb.UserId) ([]string, error)
66+
FindUsers(query string) ([]*userpb.User, error)
67+
}
68+
69+
type JSONPluginGRPC struct {
70+
plugin.Plugin
71+
Impl Manager
72+
}
73+
74+
func (p *JSONPluginGRPC) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
75+
proto.RegisterUserAPIServer(s, &GRPCServer{Impl: p.Impl})
76+
return nil
77+
}
78+
79+
func (p *JSONPluginGRPC) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
80+
return &GRPCClient{client: proto.NewUserAPIClient(c)}, nil
81+
}

‎pkg/shared/type.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package shared
2+
3+
type User struct {
4+
Id *UserId
5+
Username string
6+
Mail string
7+
MailVerified bool
8+
DisplayName string
9+
Groups []string
10+
Opaque *Opaque
11+
UidNumber int64
12+
GidNumber int64
13+
}
14+
15+
// A UserId represents a user.
16+
type UserId struct {
17+
Idp string
18+
OpaqueId string
19+
Type UserType
20+
}
21+
22+
// The type of user.
23+
type UserType int32
24+
25+
const (
26+
// The user is invalid, for example, is missing primary attributes.
27+
UserType_USER_TYPE_INVALID UserType = 0
28+
// A primary user.
29+
UserType_USER_TYPE_PRIMARY UserType = 1
30+
// A secondary user for cases with multiple identities.
31+
UserType_USER_TYPE_SECONDARY UserType = 2
32+
// A user catering to specific services.
33+
UserType_USER_TYPE_SERVICE UserType = 3
34+
// A user to be used by specific applications.
35+
UserType_USER_TYPE_APPLICATION UserType = 4
36+
// A guest user not affiliated to the IDP.
37+
UserType_USER_TYPE_GUEST UserType = 5
38+
// A federated user provided by external IDPs.
39+
UserType_USER_TYPE_FEDERATED UserType = 6
40+
// A lightweight user account without access to various major functionalities.
41+
UserType_USER_TYPE_LIGHTWEIGHT UserType = 7
42+
)
43+
44+
type Opaque struct {
45+
// REQUIRED.
46+
Map map[string]*OpaqueEntry
47+
}
48+
49+
type OpaqueEntry struct {
50+
Decoder string `protobuf:"bytes,1,opt,name=decoder,proto3" json:"decoder,omitempty"`
51+
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
52+
}

0 commit comments

Comments
 (0)
Please sign in to comment.