diff --git a/backend/identity/.gitignore b/backend/identity/.gitignore new file mode 100644 index 0000000000..29d7277364 --- /dev/null +++ b/backend/identity/.gitignore @@ -0,0 +1 @@ +identity diff --git a/backend/identity/Dockerfile b/backend/identity/Dockerfile deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/backend/identity/README.md b/backend/identity/README.md new file mode 100644 index 0000000000..45bdb4b2d1 --- /dev/null +++ b/backend/identity/README.md @@ -0,0 +1,21 @@ +# Backstage Identity Service + +Mock implementation of Backstage Indentity service API. + +## Usage + +```sh +$ go build . + +$ ./identity +``` + +Try it out from `proto/` folder: + +```sh +prototool grpc identity \ + --address 0.0.0.0:50051 \ + --method spotify.backstage.identity.v1.Identity/GetUser \ + --data '{"id":"patriko"}' \ + --details +``` diff --git a/backend/identity/go.mod b/backend/identity/go.mod index 7514099092..28d0ae93b3 100644 --- a/backend/identity/go.mod +++ b/backend/identity/go.mod @@ -5,6 +5,7 @@ go 1.13 replace github.com/spotify/backstage/proto => ../proto require ( + github.com/golang/mock v1.1.1 github.com/spotify/backstage/proto v0.0.0-00010101000000-000000000000 google.golang.org/grpc v1.27.0 ) diff --git a/backend/identity/go.sum b/backend/identity/go.sum index 7ec6345fd2..f0f642c0ae 100644 --- a/backend/identity/go.sum +++ b/backend/identity/go.sum @@ -5,12 +5,14 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/backend/identity/identity-data.json b/backend/identity/identity-data.json new file mode 100644 index 0000000000..bb33d9ce19 --- /dev/null +++ b/backend/identity/identity-data.json @@ -0,0 +1,23 @@ +{ + "users": [ + { + "id": "rugvip", + "name": "Patrik", + "groups": ["backstage"] + } + ], + "groups": [ + { + "id": "spotify", + "groups": ["backstage", "scio"] + }, + { + "id": "backstage", + "groups": [] + }, + { + "id": "scio", + "groups": [] + } + ] +} diff --git a/backend/identity/main.go b/backend/identity/main.go index ed48a3ab6f..c377954261 100644 --- a/backend/identity/main.go +++ b/backend/identity/main.go @@ -1,38 +1,35 @@ package main import ( - "context" "log" "net" + "github.com/spotify/backstage/identity/model" + "github.com/spotify/backstage/identity/service" + identityv1 "github.com/spotify/backstage/proto/identity/v1" "google.golang.org/grpc" ) const ( - port = ":50051" + port = ":50051" + dataPath = "./identity-data.json" ) func main() { + data, err := model.LoadIdentityData(dataPath) + if err != nil { + log.Fatalf("failed to load identity data, %s", err) + } + lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() - identityv1.RegisterIdentityServer(grpcServer, &server{}) + identityv1.RegisterIdentityServer(grpcServer, service.New(data)) log.Println("Serving Identity Service") grpcServer.Serve(lis) } - -type server struct { -} - -func (s *server) GetUser(ctx context.Context, req *identityv1.GetUserRequest) (*identityv1.GetUserReply, error) { - return &identityv1.GetUserReply{}, nil -} - -func (s *server) GetGroup(ctx context.Context, req *identityv1.GetGroupRequest) (*identityv1.GetGroupReply, error) { - return &identityv1.GetGroupReply{}, nil -} diff --git a/backend/identity/model/loader.go b/backend/identity/model/loader.go new file mode 100644 index 0000000000..6b4a59db36 --- /dev/null +++ b/backend/identity/model/loader.go @@ -0,0 +1,53 @@ +package model + +import ( + "encoding/json" + "io/ioutil" +) + +type IdentityFileData struct { + Users []struct { + ID string `json:"id"` + Name string `json:"name"` + Groups []string `json:"groups"` + } + Groups []struct { + ID string `json:"id"` + Groups []string `json:"groups"` + } +} + +// LoadIdentityData loads identity data from a json file. +func LoadIdentityData(path string) (*IdentityData, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + var identityFileData IdentityFileData + if err := json.Unmarshal(data, &identityFileData); err != nil { + return nil, err + } + + identityData := IdentityData{ + Users: map[string]User{}, + Groups: map[string]Group{}, + } + + for _, user := range identityFileData.Users { + identityData.Users[user.ID] = User{ + ID: user.ID, + Name: user.Name, + Groups: user.Groups, + } + } + + for _, group := range identityFileData.Groups { + identityData.Groups[group.ID] = Group{ + ID: group.ID, + Groups: group.Groups, + } + } + + return &identityData, nil +} diff --git a/backend/identity/model/model.go b/backend/identity/model/model.go new file mode 100644 index 0000000000..cca2eeba84 --- /dev/null +++ b/backend/identity/model/model.go @@ -0,0 +1,18 @@ +package model + +type IdentityData struct { + Users map[string]User + Groups map[string]Group +} + +type User struct { + ID string + Name string + Groups []string +} + +type Group struct { + ID string + Users []string + Groups []string +} diff --git a/backend/identity/service/service.go b/backend/identity/service/service.go new file mode 100644 index 0000000000..a6f0f4bfa3 --- /dev/null +++ b/backend/identity/service/service.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + + "github.com/spotify/backstage/identity/model" + identityv1 "github.com/spotify/backstage/proto/identity/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type identityDataServer model.IdentityData + +// New creates a new identity data server +func New(data *model.IdentityData) identityv1.IdentityServer { + return (*identityDataServer)(data) +} + +func (s *identityDataServer) GetUser(ctx context.Context, req *identityv1.GetUserRequest) (*identityv1.GetUserReply, error) { + id := req.GetId() + if id == "" { + return nil, status.Error(codes.InvalidArgument, "ID is required in GetUserRequest") + } + + user, ok := s.Users[id] + if !ok { + return nil, status.Errorf(codes.NotFound, "User '%s' not found", id) + } + + var replyGroups []*identityv1.Group + for _, groupID := range user.Groups { + replyGroups = append(replyGroups, &identityv1.Group{ + Id: groupID, + }) + } + + return &identityv1.GetUserReply{ + User: &identityv1.User{Id: user.ID, Name: user.Name}, + Groups: replyGroups, + }, nil +} + +func (s *identityDataServer) GetGroup(ctx context.Context, req *identityv1.GetGroupRequest) (*identityv1.GetGroupReply, error) { + id := req.GetId() + if id == "" { + return nil, status.Error(codes.InvalidArgument, "ID is required in GetGroupRequest") + } + + group, ok := s.Groups[id] + if !ok { + return nil, status.Errorf(codes.NotFound, "Group '%s' not found", id) + } + + var replyUsers []*identityv1.User + for _, userID := range group.Users { + replyUsers = append(replyUsers, &identityv1.User{ + Id: userID, + }) + } + + var replyGroups []*identityv1.Group + for _, groupID := range group.Groups { + replyGroups = append(replyGroups, &identityv1.Group{ + Id: groupID, + }) + } + + return &identityv1.GetGroupReply{ + Group: &identityv1.Group{Id: group.ID, Users: replyUsers, Groups: replyGroups}, + }, nil +} diff --git a/backend/proto/identity/v1/identity.pb.go b/backend/proto/identity/v1/identity.pb.go index e34ba12e61..c25d39e394 100644 --- a/backend/proto/identity/v1/identity.pb.go +++ b/backend/proto/identity/v1/identity.pb.go @@ -150,7 +150,7 @@ func (m *GetGroupRequest) GetId() string { } type GetGroupReply struct { - Groups *Group `protobuf:"bytes,1,opt,name=groups,proto3" json:"groups,omitempty"` + Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -181,9 +181,9 @@ func (m *GetGroupReply) XXX_DiscardUnknown() { var xxx_messageInfo_GetGroupReply proto.InternalMessageInfo -func (m *GetGroupReply) GetGroups() *Group { +func (m *GetGroupReply) GetGroup() *Group { if m != nil { - return m.Groups + return m.Group } return nil } @@ -302,7 +302,7 @@ func init() { func init() { proto.RegisterFile("identity/v1/identity.proto", fileDescriptor_2343b5ee8923b571) } var fileDescriptor_2343b5ee8923b571 = []byte{ - // 297 bytes of a gzipped FileDescriptorProto + // 303 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0x4c, 0x49, 0xcd, 0x2b, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, 0xd4, 0x87, 0xb1, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x64, 0x8b, 0x0b, 0xf2, 0x4b, 0x32, 0xd3, 0x2a, 0xf5, 0x92, 0x12, 0x93, 0xb3, 0x8b, 0x4b, @@ -312,16 +312,16 @@ var fileDescriptor_2343b5ee8923b571 = []byte{ 0x4a, 0x0a, 0x72, 0x2a, 0x85, 0xcc, 0xb9, 0x58, 0x4a, 0x8b, 0x53, 0x8b, 0xc0, 0x4a, 0xb8, 0x8d, 0x94, 0xf5, 0xf0, 0x5a, 0xa0, 0x07, 0xd6, 0x07, 0xd6, 0x20, 0x64, 0xc3, 0xc5, 0x96, 0x5e, 0x94, 0x5f, 0x5a, 0x50, 0x2c, 0xc1, 0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0x42, 0x40, 0xab, 0x3b, 0x48, - 0x71, 0x10, 0x54, 0x8f, 0x92, 0x22, 0x17, 0xbf, 0x7b, 0x6a, 0x09, 0x44, 0x0c, 0x87, 0x53, 0x7d, - 0xb9, 0x78, 0x11, 0x4a, 0x40, 0x4e, 0x45, 0xd8, 0x08, 0x71, 0x2c, 0x69, 0x36, 0x6a, 0x71, 0xb1, - 0x80, 0x5c, 0x8f, 0x6e, 0x8d, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62, 0x6e, 0xaa, 0x04, 0x13, 0x58, - 0x04, 0xcc, 0x56, 0x9a, 0xc0, 0xc8, 0xc5, 0x0a, 0xd6, 0x8d, 0xa1, 0xda, 0x92, 0x8b, 0x15, 0xe4, - 0x7b, 0x98, 0xa7, 0x89, 0x0a, 0x2f, 0x88, 0x0e, 0x24, 0xe7, 0x33, 0x93, 0x1e, 0x60, 0x46, 0xb7, - 0x19, 0xb9, 0x38, 0x3c, 0xa1, 0xb2, 0x42, 0xa9, 0x5c, 0xec, 0xd0, 0x48, 0x14, 0xd2, 0x25, 0x64, - 0x0a, 0x4a, 0x7a, 0x90, 0xd2, 0x26, 0x56, 0x39, 0x28, 0xc0, 0x33, 0xb8, 0x38, 0x60, 0x31, 0x20, - 0xa4, 0x47, 0x58, 0x23, 0x72, 0x6c, 0x4a, 0xe9, 0x10, 0xad, 0xbe, 0x20, 0xa7, 0xd2, 0x89, 0x27, - 0x8a, 0x0b, 0x26, 0x59, 0x66, 0x98, 0xc4, 0x06, 0x4e, 0xec, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x5f, 0xf8, 0xf1, 0xa3, 0x0a, 0x03, 0x00, 0x00, + 0x71, 0x10, 0x54, 0x8f, 0x92, 0x22, 0x17, 0xbf, 0x7b, 0x6a, 0x09, 0x44, 0x0c, 0x87, 0x53, 0xbd, + 0xb9, 0x78, 0x11, 0x4a, 0x40, 0x4e, 0xb5, 0xe2, 0x62, 0x05, 0xeb, 0x86, 0xba, 0x95, 0x38, 0x0b, + 0x21, 0x5a, 0x94, 0xb4, 0xb8, 0x58, 0x40, 0x6e, 0x47, 0xb7, 0x44, 0x48, 0x88, 0x8b, 0x25, 0x2f, + 0x31, 0x37, 0x55, 0x82, 0x09, 0x2c, 0x02, 0x66, 0x2b, 0x4d, 0x60, 0xe4, 0x62, 0x05, 0x6b, 0xc6, + 0x50, 0x6d, 0xc9, 0xc5, 0x0a, 0xf2, 0x3b, 0xcc, 0xcb, 0x44, 0x85, 0x16, 0x44, 0x07, 0x52, 0x70, + 0x31, 0x93, 0x1e, 0x5c, 0x46, 0xb7, 0x19, 0xb9, 0x38, 0x3c, 0xa1, 0xb2, 0x42, 0xa9, 0x5c, 0xec, + 0xd0, 0x28, 0x14, 0xd2, 0x25, 0x64, 0x0a, 0x4a, 0x6a, 0x90, 0xd2, 0x26, 0x56, 0x39, 0x28, 0xb8, + 0x33, 0xb8, 0x38, 0x60, 0xe1, 0x2f, 0xa4, 0x47, 0x58, 0x23, 0x72, 0x5c, 0x4a, 0xe9, 0x10, 0xad, + 0xbe, 0x20, 0xa7, 0xd2, 0x89, 0x27, 0x8a, 0x0b, 0x26, 0x59, 0x66, 0x98, 0xc4, 0x06, 0x4e, 0xea, + 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0xe1, 0xa3, 0xe0, 0x08, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/proto/identity/v1/identity.proto b/proto/identity/v1/identity.proto index ff3ca12cb1..4e4e8291fd 100644 --- a/proto/identity/v1/identity.proto +++ b/proto/identity/v1/identity.proto @@ -27,7 +27,7 @@ message GetGroupRequest { } message GetGroupReply { - Group groups = 1; + Group group = 1; } message User {