backend/identity: add implementation with static data

This commit is contained in:
Patrik Oldsberg
2020-02-04 13:56:18 +01:00
parent b52ece5b9b
commit d01cff2573
12 changed files with 216 additions and 29 deletions
+1
View File
@@ -0,0 +1 @@
identity
View File
+21
View File
@@ -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
```
+1
View File
@@ -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
)
+2
View File
@@ -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=
+23
View File
@@ -0,0 +1,23 @@
{
"users": [
{
"id": "rugvip",
"name": "Patrik",
"groups": ["backstage"]
}
],
"groups": [
{
"id": "spotify",
"groups": ["backstage", "scio"]
},
{
"id": "backstage",
"groups": []
},
{
"id": "scio",
"groups": []
}
]
}
+11 -14
View File
@@ -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
}
+53
View File
@@ -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
}
+18
View File
@@ -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
}
+71
View File
@@ -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
}
+14 -14
View File
@@ -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.
+1 -1
View File
@@ -27,7 +27,7 @@ message GetGroupRequest {
}
message GetGroupReply {
Group groups = 1;
Group group = 1;
}
message User {