Users bugfixes + tests (#19)

Co-authored-by: Asim Aslam <asim@aslam.me>
This commit is contained in:
Janos Dobronszki
2020-10-26 17:49:03 +01:00
committed by GitHub
parent 9601ad952c
commit 0e356aef01
9 changed files with 416 additions and 106 deletions

View File

@@ -1,10 +1,10 @@
name: Blog integration tests
name: Integration tests
on: [push]
jobs:
test:
name: Blog integration tests
name: Integration tests
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
@@ -26,7 +26,7 @@ jobs:
with:
repository: 'micro/micro'
path: 'micro'
ref: '20cabee1960e6abe8b59d8f178ddf66ad5da1097'
ref: 'v3.0.0-beta.7'
- name: Enable caching
uses: actions/cache@v2
@@ -52,5 +52,5 @@ jobs:
- name: Test Blog services
working-directory: services/test/integration
run: |
go clean -testcache && GOMAXPROCS=4 go test -timeout 15m --tags=blog -v ./...
go clean -testcache && GOMAXPROCS=4 go test -timeout 15m --tags=integration -v ./...

6
go.mod
View File

@@ -3,12 +3,12 @@ module github.com/micro/services
go 1.14
require (
github.com/go-sql-driver/mysql v1.5.0
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.4.3
github.com/google/uuid v1.1.2
github.com/gosimple/slug v1.9.0
github.com/micro/dev v0.0.0-20201023140212-49030ae8a31f
github.com/micro/go-micro/v2 v2.9.1
github.com/micro/dev v0.0.0-20201026103917-a7b0e7877fa5
github.com/micro/go-micro/v2 v2.9.1 // indirect
github.com/micro/micro/v3 v3.0.0-beta.7
github.com/miekg/dns v1.1.31 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect

2
go.sum
View File

@@ -332,6 +332,8 @@ github.com/micro/dev v0.0.0-20201022161742-461eaa38c096 h1:rzeONQC+ZgR5otGpJHb0N
github.com/micro/dev v0.0.0-20201022161742-461eaa38c096/go.mod h1:j/8E+ezN/ij7a9BXBHMKmLayFfUW1O4h/Owdv67B0X0=
github.com/micro/dev v0.0.0-20201023140212-49030ae8a31f h1:V9kC0gSrAUgXT3o5gMzQ3f1ILp8+xNUB53UVuwjH66M=
github.com/micro/dev v0.0.0-20201023140212-49030ae8a31f/go.mod h1:j/8E+ezN/ij7a9BXBHMKmLayFfUW1O4h/Owdv67B0X0=
github.com/micro/dev v0.0.0-20201026103917-a7b0e7877fa5 h1:TKVhBhhLeJyWihKdg7bDd7J+h6MWx2Y0niEMmTfyOOw=
github.com/micro/dev v0.0.0-20201026103917-a7b0e7877fa5/go.mod h1:j/8E+ezN/ij7a9BXBHMKmLayFfUW1O4h/Owdv67B0X0=
github.com/micro/go-micro v1.18.0 h1:gP70EZVHpJuUIT0YWth192JmlIci+qMOEByHm83XE9E=
github.com/micro/go-micro/v2 v2.9.1 h1:+S9koIrNWARjpP6k2TZ7kt0uC9zUJtNXzIdZTZRms7Q=
github.com/micro/go-micro/v2 v2.9.1/go.mod h1:x55ZM3Puy0FyvvkR3e0ha0xsE9DFwfPSUMWAIbFY0SY=

View File

@@ -1,4 +1,4 @@
// +build blog
// +build integration
package signup

View File

@@ -0,0 +1,235 @@
// +build integration
package signup
import (
"encoding/json"
"errors"
"math/rand"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/micro/micro/v3/test"
)
const (
retryCount = 1
)
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz")
func randStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func setupUsersTests(serv test.Server, t *test.T) {
envToConfigKey := map[string][]string{}
if err := test.Try("Set up config values", t, func() ([]byte, error) {
for envKey, configKeys := range envToConfigKey {
val := os.Getenv(envKey)
if len(val) == 0 {
t.Fatalf("'%v' flag is missing", envKey)
}
for _, configKey := range configKeys {
outp, err := serv.Command().Exec("config", "set", configKey, val)
if err != nil {
return outp, err
}
}
}
return serv.Command().Exec("config", "set", "micro.billing.max_included_services", "3")
}, 10*time.Second); err != nil {
t.Fatal(err)
return
}
services := []struct {
envVar string
deflt string
}{
{envVar: "POSTS_SVC", deflt: "../../../users"},
}
for _, v := range services {
outp, err := serv.Command().Exec("run", v.deflt)
if err != nil {
t.Fatal(string(outp))
return
}
}
if err := test.Try("Find posts and tags", t, func() ([]byte, error) {
outp, err := serv.Command().Exec("services")
if err != nil {
return outp, err
}
list := []string{"users"}
logOutp := []byte{}
fail := false
for _, s := range list {
if !strings.Contains(string(outp), s) {
o, _ := serv.Command().Exec("logs", s)
logOutp = append(logOutp, o...)
fail = true
}
}
if fail {
return append(outp, logOutp...), errors.New("Can't find required services in list")
}
return outp, err
}, 180*time.Second); err != nil {
return
}
// setup rules
// Adjust rules before we signup into a non admin account
outp, err := serv.Command().Exec("auth", "create", "rule", "--access=granted", "--scope=''", "--resource=\"service:users:*\"", "users")
if err != nil {
t.Fatalf("Error setting up rules: %v", outp)
return
}
// copy the config with the admin logged in so we can use it for reading logs
// we dont want to have an open access rule for logs as it's not how it works in live
confPath := serv.Command().Config
outp, err = exec.Command("cp", "-rf", confPath, confPath+".admin").CombinedOutput()
if err != nil {
t.Fatalf("Error copying config: %v", outp)
return
}
}
func TestUsersService(t *testing.T) {
test.TrySuite(t, testUsers, retryCount)
}
func testUsers(t *test.T) {
t.Parallel()
serv := test.NewServer(t, test.WithLogin())
defer serv.Close()
if err := serv.Run(); err != nil {
return
}
setupUsersTests(serv, t)
cmd := serv.Command()
email := "test@gmail.com"
password := "testPassw"
username := "john"
id := "7"
if err := test.Try("Save user", t, func() ([]byte, error) {
// Attention! The content must be unquoted, don't add quotes.
outp, err := cmd.Exec("users", "create", "--id="+id, "--email="+email, "--password="+password, "--username=john")
if err != nil {
outp1, _ := cmd.Exec("logs", "users")
return append(outp, outp1...), err
}
return outp, err
}, 15*time.Second); err != nil {
return
}
outp, err := cmd.Exec("users", "read", "--id="+id)
if err != nil {
t.Fatal(string(outp), err)
return
}
if !strings.Contains(string(outp), email) ||
!strings.Contains(string(outp), username) ||
!strings.Contains(string(outp), id) {
t.Fatal(string(outp))
return
}
// no password
outp, err = cmd.Exec("users", "login", "--email="+email)
if err == nil {
t.Fatal(string(outp))
return
}
// wrong password
outp, err = cmd.Exec("users", "login", "--email="+email, "--password=somethingincorrect")
if err == nil {
t.Fatal(string(outp))
return
}
outp, err = cmd.Exec("users", "login", "--username="+username, "--password="+password)
if err != nil {
t.Fatal(string(outp), err)
return
}
loginRsp := map[string]interface{}{}
err = json.Unmarshal(outp, &loginRsp)
if err != nil {
t.Fatal(err)
return
}
session, ok := loginRsp["session"].(map[string]interface{})
if !ok {
t.Fatal(string(outp))
return
}
sessionID := session["id"].(string)
sessionUsername := session["username"].(string)
if sessionUsername != username {
t.Fatal(string(outp))
return
}
if len(sessionID) == 0 {
t.Fatal(string(outp))
return
}
outp, err = cmd.Exec("users", "login", "--email="+email, "--password="+password)
if err != nil {
t.Fatal(string(outp), err)
return
}
outp, err = cmd.Exec("users", "login", "--email="+email, "--password="+password)
if err != nil {
t.Fatal(string(outp), err)
return
}
outp, err = cmd.Exec("users", "readSession", "--sessionId="+sessionID)
if err != nil {
t.Fatal(string(outp), err)
return
}
loginRsp = map[string]interface{}{}
err = json.Unmarshal(outp, &loginRsp)
if err != nil {
t.Fatal(err)
return
}
session, ok = loginRsp["session"].(map[string]interface{})
if !ok {
t.Fatal(string(outp))
return
}
sessionID = session["id"].(string)
sessionUsername = session["username"].(string)
if sessionUsername != username {
t.Fatal(string(outp))
return
}
}

View File

@@ -19,18 +19,33 @@ type Dao struct {
users model.Table
sessions model.Table
passwords model.Table
nameIndex model.Index
emailIndex model.Index
idIndex model.Index
}
func New() *Dao {
nameIndex := model.ByEquality("name")
nameIndex := model.ByEquality("username")
nameIndex.Unique = true
nameIndex.Order.Type = model.OrderTypeUnordered
emailIndex := model.ByEquality("email")
emailIndex.Unique = true
emailIndex.Order.Type = model.OrderTypeUnordered
// @todo there should be a better way to get the default index from model
// than recreating the options here
idIndex := model.ByEquality("id")
idIndex.Order.Type = model.OrderTypeUnordered
return &Dao{
users: model.NewTable(store.DefaultStore, "users", model.Indexes(nameIndex, emailIndex), nil),
sessions: model.NewTable(store.DefaultStore, "sessions", nil, nil),
passwords: model.NewTable(store.DefaultStore, "passwords", nil, nil),
users: model.NewTable(store.DefaultStore, "users", model.Indexes(nameIndex, emailIndex), nil),
sessions: model.NewTable(store.DefaultStore, "sessions", nil, nil),
passwords: model.NewTable(store.DefaultStore, "passwords", nil, nil),
nameIndex: nameIndex,
emailIndex: emailIndex,
idIndex: idIndex,
}
}
@@ -47,13 +62,13 @@ func (dao *Dao) CreateSession(sess *user.Session) error {
}
func (dao *Dao) DeleteSession(id string) error {
return dao.sessions.Delete(model.Equals("id", id))
return dao.sessions.Delete(dao.idIndex.ToQuery(id))
}
func (dao *Dao) ReadSession(id string) (*user.Session, error) {
sess := &user.Session{}
// @todo there should be a Read in the model to get rid of this pattern
return sess, dao.sessions.Read(model.Equals("id", id), &sess)
return sess, dao.sessions.Read(dao.idIndex.ToQuery(id), &sess)
}
func (dao *Dao) Create(user *user.User, salt string, password string) error {
@@ -71,7 +86,7 @@ func (dao *Dao) Create(user *user.User, salt string, password string) error {
}
func (dao *Dao) Delete(id string) error {
return dao.users.Delete(model.Equals("id", id))
return dao.users.Delete(dao.idIndex.ToQuery(id))
}
func (dao *Dao) Update(user *user.User) error {
@@ -81,15 +96,15 @@ func (dao *Dao) Update(user *user.User) error {
func (dao *Dao) Read(id string) (*user.User, error) {
user := &user.User{}
return user, dao.users.Read(model.Equals("id", id), user)
return user, dao.users.Read(dao.idIndex.ToQuery(id), user)
}
func (dao *Dao) Search(username, email string, limit, offset int64) ([]*user.User, error) {
var query model.Query
if len(username) > 0 {
query = model.Equals("name", username)
query = dao.nameIndex.ToQuery(username)
} else if len(email) > 0 {
query = model.Equals("email", email)
query = dao.emailIndex.ToQuery(email)
} else {
return nil, errors.New("username and email cannot be blank")
}
@@ -109,9 +124,9 @@ func (dao *Dao) UpdatePassword(id string, salt string, password string) error {
func (dao *Dao) SaltAndPassword(username, email string) (string, string, error) {
var query model.Query
if len(username) > 0 {
query = model.Equals("name", username)
query = dao.nameIndex.ToQuery(username)
} else if len(email) > 0 {
query = model.Equals("email", email)
query = dao.emailIndex.ToQuery(email)
} else {
return "", "", errors.New("username and email cannot be blank")
}

View File

@@ -44,16 +44,21 @@ func NewUsers() *Users {
}
func (s *Users) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
if len(req.Password) < 8 {
return errors.InternalServerError("users.Create.Check", "Password is less than 8 characters")
}
salt := random(16)
h, err := bcrypt.GenerateFromPassword([]byte(x+salt+req.Password), 10)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.Create", err.Error())
return errors.InternalServerError("users.Create", err.Error())
}
pp := base64.StdEncoding.EncodeToString(h)
req.User.Username = strings.ToLower(req.User.Username)
req.User.Email = strings.ToLower(req.User.Email)
return s.dao.Create(req.User, salt, pp)
return s.dao.Create(&pb.User{
Id: req.Id,
Username: strings.ToLower(req.Username),
Email: strings.ToLower(req.Email),
}, salt, pp)
}
func (s *Users) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
@@ -66,9 +71,11 @@ func (s *Users) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadRespo
}
func (s *Users) Update(ctx context.Context, req *pb.UpdateRequest, rsp *pb.UpdateResponse) error {
req.User.Username = strings.ToLower(req.User.Username)
req.User.Email = strings.ToLower(req.User.Email)
return s.dao.Update(req.User)
return s.dao.Update(&pb.User{
Id: req.Id,
Username: strings.ToLower(req.Username),
Email: strings.ToLower(req.Email),
})
}
func (s *Users) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
@@ -87,32 +94,35 @@ func (s *Users) Search(ctx context.Context, req *pb.SearchRequest, rsp *pb.Searc
func (s *Users) UpdatePassword(ctx context.Context, req *pb.UpdatePasswordRequest, rsp *pb.UpdatePasswordResponse) error {
usr, err := s.dao.Read(req.UserId)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.updatepassword", err.Error())
return errors.InternalServerError("users.updatepassword", err.Error())
}
if req.NewPassword != req.ConfirmPassword {
return errors.InternalServerError("users.updatepassword", "Passwords don't math")
}
salt, hashed, err := s.dao.SaltAndPassword(usr.Username, usr.Email)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.updatepassword", err.Error())
return errors.InternalServerError("users.updatepassword", err.Error())
}
hh, err := base64.StdEncoding.DecodeString(hashed)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.updatepassword", err.Error())
return errors.InternalServerError("users.updatepassword", err.Error())
}
if err := bcrypt.CompareHashAndPassword(hh, []byte(x+salt+req.OldPassword)); err != nil {
return errors.Unauthorized("go.micro.srv.user.updatepassword", err.Error())
return errors.Unauthorized("users.updatepassword", err.Error())
}
salt = random(16)
h, err := bcrypt.GenerateFromPassword([]byte(x+salt+req.NewPassword), 10)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.updatepassword", err.Error())
return errors.InternalServerError("users.updatepassword", err.Error())
}
pp := base64.StdEncoding.EncodeToString(h)
if err := s.dao.UpdatePassword(req.UserId, salt, pp); err != nil {
return errors.InternalServerError("go.micro.srv.user.updatepassword", err.Error())
return errors.InternalServerError("users.updatepassword", err.Error())
}
return nil
}
@@ -128,22 +138,23 @@ func (s *Users) Login(ctx context.Context, req *pb.LoginRequest, rsp *pb.LoginRe
hh, err := base64.StdEncoding.DecodeString(hashed)
if err != nil {
return errors.InternalServerError("go.micro.srv.user.Login", err.Error())
return errors.InternalServerError("users.Login", err.Error())
}
if err := bcrypt.CompareHashAndPassword(hh, []byte(x+salt+req.Password)); err != nil {
return errors.Unauthorized("go.micro.srv.user.login", err.Error())
return errors.Unauthorized("users.login", err.Error())
}
// save session
sess := &pb.Session{
Id: random(128),
Username: username,
Email: email,
Created: time.Now().Unix(),
Expires: time.Now().Add(time.Hour * 24 * 7).Unix(),
}
if err := s.dao.CreateSession(sess); err != nil {
return errors.InternalServerError("go.micro.srv.user.Login", err.Error())
return errors.InternalServerError("users.Login", err.Error())
}
rsp.Session = sess
return nil

View File

@@ -94,8 +94,9 @@ func (m *User) GetUpdated() int64 {
type Session struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"`
Expires int64 `protobuf:"varint,4,opt,name=expires,proto3" json:"expires,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
Created int64 `protobuf:"varint,4,opt,name=created,proto3" json:"created,omitempty"`
Expires int64 `protobuf:"varint,5,opt,name=expires,proto3" json:"expires,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -140,6 +141,13 @@ func (m *Session) GetUsername() string {
return ""
}
func (m *Session) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *Session) GetCreated() int64 {
if m != nil {
return m.Created
@@ -155,8 +163,10 @@ func (m *Session) GetExpires() int64 {
}
type CreateRequest struct {
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -187,11 +197,25 @@ func (m *CreateRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_CreateRequest proto.InternalMessageInfo
func (m *CreateRequest) GetUser() *User {
func (m *CreateRequest) GetId() string {
if m != nil {
return m.User
return m.Id
}
return nil
return ""
}
func (m *CreateRequest) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *CreateRequest) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *CreateRequest) GetPassword() string {
@@ -381,7 +405,9 @@ func (m *ReadResponse) GetUser() *User {
}
type UpdateRequest struct {
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -412,11 +438,25 @@ func (m *UpdateRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_UpdateRequest proto.InternalMessageInfo
func (m *UpdateRequest) GetUser() *User {
func (m *UpdateRequest) GetId() string {
if m != nil {
return m.User
return m.Id
}
return nil
return ""
}
func (m *UpdateRequest) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *UpdateRequest) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
type UpdateResponse struct {
@@ -454,7 +494,7 @@ type UpdatePasswordRequest struct {
UserId string `protobuf:"bytes,1,opt,name=userId,proto3" json:"userId,omitempty"`
OldPassword string `protobuf:"bytes,2,opt,name=oldPassword,proto3" json:"oldPassword,omitempty"`
NewPassword string `protobuf:"bytes,3,opt,name=newPassword,proto3" json:"newPassword,omitempty"`
ConfirmPassword string `protobuf:"bytes,4,opt,name=confirmPassword,proto3" json:"confirmPassword,omitempty"`
ConfirmPassword string `protobuf:"bytes,4,opt,name=confirm_password,json=confirmPassword,proto3" json:"confirm_password,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -911,46 +951,48 @@ func init() {
proto.RegisterType((*LogoutResponse)(nil), "LogoutResponse")
}
func init() { proto.RegisterFile("proto/users.proto", fileDescriptor_b1c161a4c7514913) }
func init() {
proto.RegisterFile("proto/users.proto", fileDescriptor_b1c161a4c7514913)
}
var fileDescriptor_b1c161a4c7514913 = []byte{
// 605 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x4f, 0x6f, 0xd3, 0x4e,
0x10, 0xb5, 0xe3, 0x38, 0x69, 0x27, 0xb1, 0xdd, 0xdf, 0x36, 0xbf, 0x60, 0x0c, 0x88, 0x68, 0x25,
0xa4, 0x00, 0xea, 0x20, 0xa5, 0x27, 0xb8, 0x16, 0x21, 0x21, 0x71, 0xa8, 0x52, 0xf5, 0xc6, 0xc5,
0xc4, 0x1b, 0xb0, 0x94, 0x78, 0x83, 0xd7, 0x51, 0x39, 0xf0, 0x55, 0xf8, 0x22, 0x7c, 0x3a, 0xb4,
0xff, 0x12, 0xdb, 0x6d, 0x80, 0x72, 0xcb, 0xcc, 0xbc, 0xdd, 0x79, 0x3b, 0xf3, 0x9e, 0x03, 0xff,
0x6d, 0x4a, 0x5e, 0xf1, 0x57, 0x5b, 0xc1, 0x4a, 0x81, 0xea, 0x37, 0xfd, 0x0e, 0xdd, 0x6b, 0xc1,
0x4a, 0x12, 0x42, 0x27, 0xcf, 0x62, 0x77, 0xe2, 0x4e, 0x8f, 0xe7, 0x9d, 0x3c, 0x23, 0x09, 0x1c,
0x49, 0x58, 0x91, 0xae, 0x59, 0xdc, 0x51, 0xd9, 0x5d, 0x4c, 0x46, 0xe0, 0xb3, 0x75, 0x9a, 0xaf,
0x62, 0x4f, 0x15, 0x74, 0x40, 0x62, 0xe8, 0x2f, 0x4a, 0x96, 0x56, 0x2c, 0x8b, 0xbb, 0x13, 0x77,
0xea, 0xcd, 0x6d, 0x28, 0x2b, 0xdb, 0x4d, 0xa6, 0x2a, 0xbe, 0xae, 0x98, 0x90, 0xe6, 0xd0, 0xbf,
0x62, 0x42, 0xe4, 0xbc, 0xb8, 0x17, 0x81, 0x5a, 0x2b, 0xef, 0x56, 0x2b, 0xf6, 0x6d, 0x93, 0x97,
0x4c, 0x58, 0x12, 0x26, 0xa4, 0xef, 0x20, 0xb8, 0x50, 0xa0, 0x39, 0xfb, 0xba, 0x65, 0xa2, 0x22,
0x0f, 0xa1, 0x2b, 0x2f, 0x54, 0x2d, 0x07, 0x33, 0x1f, 0xe5, 0x18, 0xe6, 0x2a, 0x25, 0x7b, 0x6f,
0x52, 0x21, 0x6e, 0x78, 0x99, 0xd9, 0xde, 0x36, 0xa6, 0x27, 0x10, 0xda, 0x7b, 0xc4, 0x86, 0x17,
0x82, 0xd1, 0xa7, 0x10, 0xbc, 0x65, 0x2b, 0xb6, 0xbf, 0xb9, 0xf5, 0x14, 0x79, 0xc4, 0x02, 0xcc,
0x91, 0x27, 0x30, 0x98, 0xb3, 0x34, 0x3b, 0x74, 0xe0, 0x39, 0x0c, 0x75, 0x59, 0xc3, 0x7f, 0x43,
0x95, 0xbe, 0x80, 0xe0, 0x5a, 0x0d, 0xf3, 0xcf, 0xcf, 0x92, 0x3c, 0x2c, 0xd6, 0xf0, 0xf8, 0xe1,
0xc2, 0xff, 0x3a, 0x75, 0x69, 0xde, 0x67, 0xaf, 0x19, 0x43, 0x4f, 0x9e, 0x79, 0x6f, 0x69, 0x99,
0x88, 0x4c, 0x60, 0xc0, 0x57, 0xd9, 0x65, 0x73, 0x3a, 0xf5, 0x94, 0x44, 0x14, 0xec, 0x66, 0x87,
0xd0, 0x1a, 0xa9, 0xa7, 0xc8, 0x14, 0xa2, 0x05, 0x2f, 0x96, 0x79, 0xb9, 0xde, 0xa1, 0xba, 0x0a,
0xd5, 0x4e, 0xd3, 0x18, 0xc6, 0x6d, 0x7a, 0x86, 0x39, 0x87, 0xe0, 0x8a, 0xa5, 0xe5, 0xe2, 0x8b,
0x25, 0x5c, 0xd7, 0x8b, 0x7b, 0x48, 0xb0, 0x9d, 0xba, 0x60, 0x47, 0xe0, 0xaf, 0xf2, 0x75, 0x5e,
0x19, 0x0d, 0xe9, 0x40, 0x3e, 0x9c, 0x2f, 0x97, 0x82, 0x55, 0x46, 0x40, 0x26, 0xa2, 0x67, 0x10,
0xda, 0x86, 0x66, 0x2b, 0x8f, 0xc0, 0x57, 0x4e, 0x8a, 0xdd, 0x89, 0xb7, 0x1f, 0xb5, 0xce, 0xd1,
0x19, 0x10, 0xb9, 0x42, 0xa3, 0x6e, 0x4b, 0xf2, 0x31, 0x1c, 0x0b, 0x9d, 0xd9, 0x0d, 0x76, 0x9f,
0xa0, 0xaf, 0xe1, 0xb4, 0x71, 0xc6, 0xf4, 0xa1, 0xd0, 0x37, 0x18, 0xb3, 0xd4, 0x23, 0xb4, 0x10,
0x5b, 0xa0, 0x1f, 0x61, 0xf8, 0x81, 0x7f, 0xce, 0x8b, 0x7f, 0x9f, 0x46, 0x5d, 0xf3, 0x5e, 0x4b,
0xf3, 0xe7, 0x10, 0x98, 0xdb, 0xef, 0x41, 0xe9, 0x4c, 0x1d, 0xe2, 0xdb, 0xea, 0xef, 0x1e, 0x7f,
0x02, 0xa1, 0x85, 0xeb, 0x26, 0xb3, 0x9f, 0x1e, 0xf8, 0x72, 0xa4, 0x82, 0xbc, 0x84, 0x9e, 0xf6,
0x1c, 0x09, 0xb1, 0x61, 0xe2, 0x24, 0xc2, 0x96, 0x19, 0x1d, 0xf2, 0x0c, 0xba, 0x72, 0x8a, 0x64,
0x88, 0x35, 0x8b, 0x25, 0x01, 0xd6, 0x1d, 0x45, 0x1d, 0x79, 0xa7, 0x96, 0x16, 0x09, 0xb1, 0xe1,
0xa0, 0x24, 0xc2, 0x96, 0x4b, 0x14, 0x58, 0x3b, 0x98, 0x84, 0xd8, 0xf0, 0x7a, 0x12, 0x61, 0xcb,
0xda, 0x0a, 0xac, 0x95, 0x42, 0x42, 0x6c, 0x68, 0x34, 0x89, 0xb0, 0x29, 0x21, 0xea, 0x90, 0x0b,
0xeb, 0xc9, 0x9d, 0x3b, 0xc6, 0x78, 0xa7, 0x23, 0x93, 0x07, 0x78, 0xc0, 0x0a, 0x0e, 0x99, 0x82,
0xaf, 0xf6, 0x43, 0x02, 0xac, 0xab, 0x20, 0x09, 0xb1, 0xb1, 0x36, 0xcd, 0x4d, 0x4f, 0x99, 0xa8,
0xda, 0x7e, 0x3b, 0x49, 0x84, 0xcd, 0xf1, 0x53, 0x87, 0xbc, 0xd1, 0x5f, 0x29, 0xfb, 0x85, 0x3e,
0xc5, 0xdb, 0x8a, 0x4e, 0x46, 0x78, 0x87, 0x64, 0xa9, 0xf3, 0xa9, 0xa7, 0xfe, 0x5e, 0xce, 0x7f,
0x05, 0x00, 0x00, 0xff, 0xff, 0x41, 0x5e, 0x5d, 0x7f, 0x73, 0x06, 0x00, 0x00,
// 600 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x55, 0xcf, 0x6e, 0xd3, 0x4e,
0x10, 0x4e, 0xe2, 0x38, 0x69, 0x27, 0x71, 0xda, 0xdf, 0xb6, 0xbf, 0x60, 0x0c, 0x88, 0x6a, 0x25,
0xa4, 0x56, 0xa8, 0x8b, 0x94, 0x9e, 0xe0, 0x5a, 0x2e, 0x48, 0x1c, 0xc0, 0x55, 0x6f, 0x48, 0xc8,
0x24, 0x1b, 0xb0, 0x14, 0x7b, 0x8d, 0xd7, 0x51, 0x39, 0x20, 0xf1, 0x26, 0x3c, 0x08, 0x4f, 0xc7,
0xec, 0xbf, 0xc4, 0x36, 0x8d, 0x04, 0xa8, 0xdc, 0x3c, 0x33, 0xdf, 0xec, 0x7c, 0x3b, 0x33, 0xdf,
0x1a, 0xfe, 0x2b, 0x4a, 0x51, 0x89, 0x67, 0x6b, 0xc9, 0x4b, 0xc9, 0xf4, 0x37, 0xfd, 0x0a, 0xfd,
0x6b, 0x34, 0xc9, 0x04, 0x7a, 0xe9, 0x22, 0xec, 0x9e, 0x74, 0x4f, 0xf7, 0x63, 0xfc, 0x22, 0x11,
0xec, 0x29, 0x58, 0x9e, 0x64, 0x3c, 0xec, 0x69, 0xef, 0xc6, 0x26, 0xc7, 0xe0, 0xf3, 0x2c, 0x49,
0x57, 0xa1, 0xa7, 0x03, 0xc6, 0x20, 0x21, 0x0c, 0xe7, 0x25, 0x4f, 0x2a, 0xbe, 0x08, 0xfb, 0xe8,
0xf7, 0x62, 0x67, 0xaa, 0xc8, 0xba, 0x58, 0xe8, 0x88, 0x6f, 0x22, 0xd6, 0xa4, 0xdf, 0x60, 0x78,
0xc5, 0xa5, 0x4c, 0x45, 0xfe, 0xaf, 0x09, 0xf0, 0x2f, 0x45, 0x5a, 0x72, 0xe9, 0x08, 0x58, 0x93,
0x66, 0x10, 0x5c, 0x6a, 0x50, 0xcc, 0x3f, 0xaf, 0xb9, 0xac, 0xee, 0x80, 0x06, 0x66, 0x14, 0x89,
0x94, 0x37, 0xa2, 0x34, 0x3c, 0x30, 0xc3, 0xd9, 0xf4, 0x10, 0x26, 0xae, 0x9c, 0x2c, 0x44, 0x2e,
0x39, 0x7d, 0x0c, 0xc1, 0x4b, 0xbe, 0xe2, 0x3b, 0x09, 0xa8, 0x14, 0x07, 0xb0, 0x29, 0x8f, 0x60,
0x14, 0xf3, 0x64, 0xb1, 0x2b, 0xe1, 0x0c, 0xc6, 0x26, 0x6c, 0xe0, 0xe4, 0x3e, 0xf4, 0x15, 0x63,
0x8d, 0x18, 0xcd, 0x7c, 0xa6, 0xc6, 0x1d, 0x6b, 0x17, 0x7d, 0x0b, 0xc1, 0xb5, 0x9e, 0xc4, 0x9d,
0xdd, 0x5e, 0xd1, 0x75, 0x47, 0x5a, 0xba, 0xdf, 0xbb, 0xf0, 0xbf, 0x71, 0xbd, 0xb1, 0x6d, 0x70,
0xd5, 0xa6, 0x30, 0x50, 0xa7, 0xbd, 0x72, 0x15, 0xad, 0x45, 0x4e, 0x60, 0x24, 0x56, 0x0b, 0x87,
0xb6, 0x85, 0xeb, 0x2e, 0x85, 0xc8, 0xf9, 0xcd, 0x06, 0x61, 0x18, 0xd4, 0x5d, 0xe4, 0x0c, 0x0e,
0xe7, 0x22, 0x5f, 0xa6, 0x65, 0xf6, 0xbe, 0x35, 0x8d, 0x03, 0xeb, 0x77, 0x50, 0x1a, 0xc2, 0xb4,
0xcd, 0xcf, 0x52, 0x17, 0x10, 0x5c, 0xf1, 0xa4, 0x9c, 0x7f, 0x72, 0x8c, 0xeb, 0xfd, 0xe8, 0xee,
0xea, 0x47, 0xaf, 0xbe, 0x0d, 0xe8, 0x5d, 0xa5, 0x59, 0x5a, 0x69, 0x8e, 0x5e, 0x6c, 0x0c, 0x75,
0x73, 0xb1, 0x5c, 0x4a, 0x5e, 0xd9, 0x4d, 0xb5, 0x16, 0x3d, 0x87, 0x89, 0x2b, 0x68, 0xa7, 0xf7,
0x00, 0x7c, 0x2d, 0x57, 0x2c, 0xe7, 0x6d, 0xc7, 0x67, 0x7c, 0x74, 0x06, 0x44, 0x8d, 0xda, 0x4a,
0xc8, 0x91, 0x7c, 0x08, 0xfb, 0xd2, 0x78, 0x36, 0x9d, 0xdd, 0x3a, 0xe8, 0x73, 0x38, 0x6a, 0xe4,
0xd8, 0x3a, 0x14, 0x86, 0x16, 0x63, 0x17, 0x65, 0x8f, 0x39, 0x88, 0x0b, 0xd0, 0x77, 0x30, 0x7e,
0x2d, 0x3e, 0xa6, 0xf9, 0xdf, 0x77, 0xa3, 0xae, 0x0d, 0xaf, 0xa5, 0x8d, 0x0b, 0x08, 0xec, 0xe9,
0x7f, 0x40, 0xe9, 0x5c, 0x27, 0x89, 0x75, 0xf5, 0x7b, 0x97, 0xc7, 0xed, 0x74, 0x70, 0x53, 0x64,
0xf6, 0xc3, 0x03, 0x5f, 0xb5, 0x54, 0x92, 0xa7, 0x30, 0x30, 0xda, 0x24, 0x13, 0xd6, 0x78, 0x13,
0xa2, 0x03, 0xd6, 0x12, 0x6d, 0x87, 0x3c, 0x81, 0xbe, 0xea, 0x22, 0x19, 0xb3, 0x9a, 0x14, 0xa3,
0x80, 0xd5, 0x95, 0x87, 0x30, 0x3c, 0xd3, 0xac, 0x16, 0x9e, 0xd9, 0x50, 0x1a, 0x9e, 0xd9, 0x92,
0x89, 0x06, 0x1b, 0xa5, 0x23, 0xb8, 0xf1, 0x26, 0x20, 0xb8, 0xf5, 0x04, 0x68, 0xb0, 0xd9, 0x14,
0x04, 0x37, 0x76, 0x14, 0xc1, 0xcd, 0x15, 0x42, 0xf0, 0xa5, 0x13, 0xe5, 0x46, 0x1e, 0x53, 0x76,
0xab, 0x24, 0xa3, 0x7b, 0x6c, 0x87, 0x14, 0x3a, 0xe4, 0x14, 0x7c, 0x3d, 0x1f, 0x12, 0xb0, 0xfa,
0x16, 0x44, 0x13, 0xd6, 0x18, 0x9b, 0xe1, 0x66, 0xba, 0x4c, 0x74, 0x6c, 0x3b, 0x1d, 0xe4, 0xd6,
0x6c, 0x3f, 0x82, 0x5f, 0x98, 0xd7, 0xcc, 0xfd, 0x06, 0x8e, 0xd8, 0xaf, 0x1b, 0x1d, 0x1d, 0xb3,
0x5b, 0x56, 0x96, 0x76, 0x3e, 0x0c, 0xf4, 0x3f, 0xec, 0xe2, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff,
0xc7, 0x99, 0xc1, 0x0c, 0xd8, 0x06, 0x00, 0x00,
}

View File

@@ -21,15 +21,18 @@ message User {
}
message Session {
string id = 1;
string username = 2;
int64 created = 3; // unix
int64 expires = 4; // unix
string id = 1;
string username = 2;
string email = 3;
int64 created = 4; // unix
int64 expires = 5; // unix
}
message CreateRequest {
User user = 1;
string password = 2;
string id = 1; // uuid
string username = 2; // alphanumeric user or org
string email = 3;
string password = 4;
}
message CreateResponse {
@@ -51,17 +54,19 @@ message ReadResponse {
}
message UpdateRequest {
User user = 1;
string id = 1; // uuid
string username = 2; // alphanumeric user or org
string email = 3;
}
message UpdateResponse {
}
message UpdatePasswordRequest {
string userId = 1;
string oldPassword = 2;
string newPassword = 3;
string confirmPassword = 4;
string userId = 1;
string oldPassword = 2;
string newPassword = 3;
string confirm_password = 4;
}
message UpdatePasswordResponse {
@@ -79,25 +84,25 @@ message SearchResponse {
}
message ReadSessionRequest {
string sessionId = 1;
string sessionId = 1;
}
message ReadSessionResponse {
Session session = 1;
Session session = 1;
}
message LoginRequest {
string username = 1;
string email = 2;
string password = 3;
string username = 1;
string email = 2;
string password = 3;
}
message LoginResponse {
Session session = 1;
Session session = 1;
}
message LogoutRequest {
string sessionId = 1;
string sessionId = 1;
}
message LogoutResponse {