api: add gRPC endpoints for creating, updating and deleting passwords
This commit is contained in:
		
							
								
								
									
										260
									
								
								api/api.pb.go
									
									
									
									
									
								
							
							
						
						
									
										260
									
								
								api/api.pb.go
									
									
									
									
									
								
							| @@ -14,6 +14,13 @@ It has these top-level messages: | |||||||
| 	CreateClientResp | 	CreateClientResp | ||||||
| 	DeleteClientReq | 	DeleteClientReq | ||||||
| 	DeleteClientResp | 	DeleteClientResp | ||||||
|  | 	Password | ||||||
|  | 	CreatePasswordReq | ||||||
|  | 	CreatePasswordResp | ||||||
|  | 	UpdatePasswordReq | ||||||
|  | 	UpdatePasswordResp | ||||||
|  | 	DeletePasswordReq | ||||||
|  | 	DeletePasswordResp | ||||||
| */ | */ | ||||||
| package api | package api | ||||||
|  |  | ||||||
| @@ -109,12 +116,103 @@ func (m *DeleteClientResp) String() string            { return proto.CompactText | |||||||
| func (*DeleteClientResp) ProtoMessage()               {} | func (*DeleteClientResp) ProtoMessage()               {} | ||||||
| func (*DeleteClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } | func (*DeleteClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } | ||||||
|  |  | ||||||
|  | // Password is an email for password mapping managed by the storage. | ||||||
|  | type Password struct { | ||||||
|  | 	Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` | ||||||
|  | 	// Currently we do not accept plain text passwords. Could be an option in the future. | ||||||
|  | 	Hash     []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` | ||||||
|  | 	Username string `protobuf:"bytes,3,opt,name=username" json:"username,omitempty"` | ||||||
|  | 	UserId   string `protobuf:"bytes,4,opt,name=user_id,json=userId" json:"user_id,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Password) Reset()                    { *m = Password{} } | ||||||
|  | func (m *Password) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*Password) ProtoMessage()               {} | ||||||
|  | func (*Password) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } | ||||||
|  |  | ||||||
|  | // CreatePasswordReq is a request to make a password. | ||||||
|  | type CreatePasswordReq struct { | ||||||
|  | 	Password *Password `protobuf:"bytes,1,opt,name=password" json:"password,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CreatePasswordReq) Reset()                    { *m = CreatePasswordReq{} } | ||||||
|  | func (m *CreatePasswordReq) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*CreatePasswordReq) ProtoMessage()               {} | ||||||
|  | func (*CreatePasswordReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } | ||||||
|  |  | ||||||
|  | func (m *CreatePasswordReq) GetPassword() *Password { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.Password | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreatePasswordResp returns the response from creating a password. | ||||||
|  | type CreatePasswordResp struct { | ||||||
|  | 	AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists" json:"already_exists,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CreatePasswordResp) Reset()                    { *m = CreatePasswordResp{} } | ||||||
|  | func (m *CreatePasswordResp) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*CreatePasswordResp) ProtoMessage()               {} | ||||||
|  | func (*CreatePasswordResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } | ||||||
|  |  | ||||||
|  | // UpdatePasswordReq is a request to modify an existing password. | ||||||
|  | type UpdatePasswordReq struct { | ||||||
|  | 	// The email used to lookup the password. This field cannot be modified | ||||||
|  | 	Email       string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` | ||||||
|  | 	NewHash     []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"` | ||||||
|  | 	NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername" json:"new_username,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *UpdatePasswordReq) Reset()                    { *m = UpdatePasswordReq{} } | ||||||
|  | func (m *UpdatePasswordReq) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*UpdatePasswordReq) ProtoMessage()               {} | ||||||
|  | func (*UpdatePasswordReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } | ||||||
|  |  | ||||||
|  | // UpdatePasswordResp returns the response from modifying an existing password. | ||||||
|  | type UpdatePasswordResp struct { | ||||||
|  | 	NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound" json:"not_found,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *UpdatePasswordResp) Reset()                    { *m = UpdatePasswordResp{} } | ||||||
|  | func (m *UpdatePasswordResp) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*UpdatePasswordResp) ProtoMessage()               {} | ||||||
|  | func (*UpdatePasswordResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } | ||||||
|  |  | ||||||
|  | // DeletePasswordReq is a request to delete a password. | ||||||
|  | type DeletePasswordReq struct { | ||||||
|  | 	Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *DeletePasswordReq) Reset()                    { *m = DeletePasswordReq{} } | ||||||
|  | func (m *DeletePasswordReq) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*DeletePasswordReq) ProtoMessage()               {} | ||||||
|  | func (*DeletePasswordReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } | ||||||
|  |  | ||||||
|  | // DeletePasswordResp returns the response from deleting a password. | ||||||
|  | type DeletePasswordResp struct { | ||||||
|  | 	NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound" json:"not_found,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *DeletePasswordResp) Reset()                    { *m = DeletePasswordResp{} } | ||||||
|  | func (m *DeletePasswordResp) String() string            { return proto.CompactTextString(m) } | ||||||
|  | func (*DeletePasswordResp) ProtoMessage()               {} | ||||||
|  | func (*DeletePasswordResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	proto.RegisterType((*Client)(nil), "api.Client") | 	proto.RegisterType((*Client)(nil), "api.Client") | ||||||
| 	proto.RegisterType((*CreateClientReq)(nil), "api.CreateClientReq") | 	proto.RegisterType((*CreateClientReq)(nil), "api.CreateClientReq") | ||||||
| 	proto.RegisterType((*CreateClientResp)(nil), "api.CreateClientResp") | 	proto.RegisterType((*CreateClientResp)(nil), "api.CreateClientResp") | ||||||
| 	proto.RegisterType((*DeleteClientReq)(nil), "api.DeleteClientReq") | 	proto.RegisterType((*DeleteClientReq)(nil), "api.DeleteClientReq") | ||||||
| 	proto.RegisterType((*DeleteClientResp)(nil), "api.DeleteClientResp") | 	proto.RegisterType((*DeleteClientResp)(nil), "api.DeleteClientResp") | ||||||
|  | 	proto.RegisterType((*Password)(nil), "api.Password") | ||||||
|  | 	proto.RegisterType((*CreatePasswordReq)(nil), "api.CreatePasswordReq") | ||||||
|  | 	proto.RegisterType((*CreatePasswordResp)(nil), "api.CreatePasswordResp") | ||||||
|  | 	proto.RegisterType((*UpdatePasswordReq)(nil), "api.UpdatePasswordReq") | ||||||
|  | 	proto.RegisterType((*UpdatePasswordResp)(nil), "api.UpdatePasswordResp") | ||||||
|  | 	proto.RegisterType((*DeletePasswordReq)(nil), "api.DeletePasswordReq") | ||||||
|  | 	proto.RegisterType((*DeletePasswordResp)(nil), "api.DeletePasswordResp") | ||||||
| } | } | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. | // Reference imports to suppress errors if they are not otherwise used. | ||||||
| @@ -132,6 +230,12 @@ type DexClient interface { | |||||||
| 	CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) | 	CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) | ||||||
| 	// DeleteClient attempts to delete the provided client. | 	// DeleteClient attempts to delete the provided client. | ||||||
| 	DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) | 	DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) | ||||||
|  | 	// CreatePassword attempts to create the password. | ||||||
|  | 	CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) | ||||||
|  | 	// UpdatePassword attempts to modify existing password. | ||||||
|  | 	UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) | ||||||
|  | 	// DeletePassword attempts to delete the password. | ||||||
|  | 	DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type dexClient struct { | type dexClient struct { | ||||||
| @@ -160,6 +264,33 @@ func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts | |||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) { | ||||||
|  | 	out := new(CreatePasswordResp) | ||||||
|  | 	err := grpc.Invoke(ctx, "/api.Dex/CreatePassword", in, out, c.cc, opts...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return out, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) { | ||||||
|  | 	out := new(UpdatePasswordResp) | ||||||
|  | 	err := grpc.Invoke(ctx, "/api.Dex/UpdatePassword", in, out, c.cc, opts...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return out, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) { | ||||||
|  | 	out := new(DeletePasswordResp) | ||||||
|  | 	err := grpc.Invoke(ctx, "/api.Dex/DeletePassword", in, out, c.cc, opts...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return out, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // Server API for Dex service | // Server API for Dex service | ||||||
|  |  | ||||||
| type DexServer interface { | type DexServer interface { | ||||||
| @@ -167,6 +298,12 @@ type DexServer interface { | |||||||
| 	CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) | 	CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) | ||||||
| 	// DeleteClient attempts to delete the provided client. | 	// DeleteClient attempts to delete the provided client. | ||||||
| 	DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error) | 	DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error) | ||||||
|  | 	// CreatePassword attempts to create the password. | ||||||
|  | 	CreatePassword(context.Context, *CreatePasswordReq) (*CreatePasswordResp, error) | ||||||
|  | 	// UpdatePassword attempts to modify existing password. | ||||||
|  | 	UpdatePassword(context.Context, *UpdatePasswordReq) (*UpdatePasswordResp, error) | ||||||
|  | 	// DeletePassword attempts to delete the password. | ||||||
|  | 	DeletePassword(context.Context, *DeletePasswordReq) (*DeletePasswordResp, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| func RegisterDexServer(s *grpc.Server, srv DexServer) { | func RegisterDexServer(s *grpc.Server, srv DexServer) { | ||||||
| @@ -209,6 +346,60 @@ func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(in | |||||||
| 	return interceptor(ctx, in, info, handler) | 	return interceptor(ctx, in, info, handler) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||||
|  | 	in := new(CreatePasswordReq) | ||||||
|  | 	if err := dec(in); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if interceptor == nil { | ||||||
|  | 		return srv.(DexServer).CreatePassword(ctx, in) | ||||||
|  | 	} | ||||||
|  | 	info := &grpc.UnaryServerInfo{ | ||||||
|  | 		Server:     srv, | ||||||
|  | 		FullMethod: "/api.Dex/CreatePassword", | ||||||
|  | 	} | ||||||
|  | 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||||
|  | 		return srv.(DexServer).CreatePassword(ctx, req.(*CreatePasswordReq)) | ||||||
|  | 	} | ||||||
|  | 	return interceptor(ctx, in, info, handler) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func _Dex_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||||
|  | 	in := new(UpdatePasswordReq) | ||||||
|  | 	if err := dec(in); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if interceptor == nil { | ||||||
|  | 		return srv.(DexServer).UpdatePassword(ctx, in) | ||||||
|  | 	} | ||||||
|  | 	info := &grpc.UnaryServerInfo{ | ||||||
|  | 		Server:     srv, | ||||||
|  | 		FullMethod: "/api.Dex/UpdatePassword", | ||||||
|  | 	} | ||||||
|  | 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||||
|  | 		return srv.(DexServer).UpdatePassword(ctx, req.(*UpdatePasswordReq)) | ||||||
|  | 	} | ||||||
|  | 	return interceptor(ctx, in, info, handler) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func _Dex_DeletePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||||
|  | 	in := new(DeletePasswordReq) | ||||||
|  | 	if err := dec(in); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if interceptor == nil { | ||||||
|  | 		return srv.(DexServer).DeletePassword(ctx, in) | ||||||
|  | 	} | ||||||
|  | 	info := &grpc.UnaryServerInfo{ | ||||||
|  | 		Server:     srv, | ||||||
|  | 		FullMethod: "/api.Dex/DeletePassword", | ||||||
|  | 	} | ||||||
|  | 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||||
|  | 		return srv.(DexServer).DeletePassword(ctx, req.(*DeletePasswordReq)) | ||||||
|  | 	} | ||||||
|  | 	return interceptor(ctx, in, info, handler) | ||||||
|  | } | ||||||
|  |  | ||||||
| var _Dex_serviceDesc = grpc.ServiceDesc{ | var _Dex_serviceDesc = grpc.ServiceDesc{ | ||||||
| 	ServiceName: "api.Dex", | 	ServiceName: "api.Dex", | ||||||
| 	HandlerType: (*DexServer)(nil), | 	HandlerType: (*DexServer)(nil), | ||||||
| @@ -221,6 +412,18 @@ var _Dex_serviceDesc = grpc.ServiceDesc{ | |||||||
| 			MethodName: "DeleteClient", | 			MethodName: "DeleteClient", | ||||||
| 			Handler:    _Dex_DeleteClient_Handler, | 			Handler:    _Dex_DeleteClient_Handler, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			MethodName: "CreatePassword", | ||||||
|  | 			Handler:    _Dex_CreatePassword_Handler, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			MethodName: "UpdatePassword", | ||||||
|  | 			Handler:    _Dex_UpdatePassword_Handler, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			MethodName: "DeletePassword", | ||||||
|  | 			Handler:    _Dex_DeletePassword_Handler, | ||||||
|  | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	Streams:  []grpc.StreamDesc{}, | 	Streams:  []grpc.StreamDesc{}, | ||||||
| 	Metadata: fileDescriptor0, | 	Metadata: fileDescriptor0, | ||||||
| @@ -229,27 +432,38 @@ var _Dex_serviceDesc = grpc.ServiceDesc{ | |||||||
| func init() { proto.RegisterFile("api/api.proto", fileDescriptor0) } | func init() { proto.RegisterFile("api/api.proto", fileDescriptor0) } | ||||||
|  |  | ||||||
| var fileDescriptor0 = []byte{ | var fileDescriptor0 = []byte{ | ||||||
| 	// 337 bytes of a gzipped FileDescriptorProto | 	// 527 bytes of a gzipped FileDescriptorProto | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0x4f, 0x6b, 0xe3, 0x30, | 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x54, 0xcb, 0x6e, 0xdb, 0x30, | ||||||
| 	0x10, 0xc5, 0x63, 0x3b, 0xeb, 0x38, 0x93, 0xbf, 0x88, 0xdd, 0xc5, 0xbb, 0xbd, 0xa4, 0x0e, 0x85, | 	0x10, 0x8c, 0x1f, 0xb1, 0xe5, 0xf5, 0x23, 0x31, 0x91, 0x26, 0x8a, 0x7b, 0x71, 0x18, 0x14, 0x70, | ||||||
| 	0x9c, 0x12, 0x48, 0xa1, 0xb7, 0x9e, 0x92, 0xf6, 0x5c, 0x0c, 0xb9, 0xd6, 0x28, 0xf6, 0xb4, 0x08, | 	0x2e, 0x09, 0x9a, 0x02, 0xbd, 0x14, 0xed, 0xc5, 0x69, 0xd1, 0xde, 0x02, 0x01, 0xbe, 0x56, 0x60, | ||||||
| 	0x54, 0x4b, 0x95, 0x64, 0x48, 0xcf, 0xfd, 0x64, 0xfd, 0x66, 0x45, 0x8a, 0x0a, 0x4e, 0x68, 0x6f, | 	0xcc, 0x6d, 0x42, 0x40, 0x91, 0x58, 0x92, 0x82, 0xd3, 0xcf, 0xeb, 0x2f, 0xf4, 0x8b, 0x0a, 0x52, | ||||||
| 	0x7e, 0x3f, 0xcd, 0xcc, 0x7b, 0x33, 0x18, 0x46, 0x54, 0xb2, 0x15, 0x95, 0x6c, 0x29, 0x95, 0x30, | 	0xb4, 0x21, 0xc9, 0x2e, 0xdc, 0x9b, 0x66, 0xb8, 0x9c, 0xe5, 0xcc, 0x2e, 0x04, 0x43, 0x26, 0xc5, | ||||||
| 	0x82, 0x44, 0x54, 0xb2, 0xec, 0x23, 0x80, 0x78, 0xc3, 0x19, 0xd6, 0x86, 0x8c, 0x21, 0x64, 0x55, | 	0x0d, 0x93, 0xe2, 0x5a, 0xaa, 0xcc, 0x64, 0xa4, 0xc5, 0xa4, 0xa0, 0xbf, 0x1b, 0xd0, 0x99, 0x27, | ||||||
| 	0x1a, 0xcc, 0x82, 0x45, 0x3f, 0x0f, 0x59, 0x45, 0xfe, 0x42, 0xac, 0xb1, 0x54, 0x68, 0xd2, 0xd0, | 	0x02, 0x53, 0x43, 0x46, 0xd0, 0x14, 0x3c, 0x6c, 0x4c, 0x1b, 0xb3, 0x5e, 0xd4, 0x14, 0x9c, 0x9c, | ||||||
| 	0x31, 0xaf, 0xc8, 0x1c, 0x46, 0x0a, 0x2b, 0xa6, 0xb0, 0x34, 0x45, 0xa3, 0x98, 0x4e, 0xa3, 0x59, | 	0x42, 0x47, 0xe3, 0x52, 0xa1, 0x09, 0x9b, 0x8e, 0xf3, 0x88, 0x5c, 0xc2, 0x50, 0x21, 0x17, 0x0a, | ||||||
| 	0xb4, 0xe8, 0xe7, 0xc3, 0x2f, 0xb8, 0x53, 0x4c, 0xdb, 0x22, 0xa3, 0x1a, 0x6d, 0xb0, 0x2a, 0x24, | 	0x97, 0x26, 0xce, 0x95, 0xd0, 0x61, 0x6b, 0xda, 0x9a, 0xf5, 0xa2, 0xc1, 0x9a, 0x5c, 0x28, 0xa1, | ||||||
| 	0xa2, 0xd2, 0x69, 0xf7, 0x58, 0xe4, 0xe1, 0x83, 0x65, 0xd6, 0x41, 0x36, 0x7b, 0xce, 0xca, 0xf4, | 	0x6d, 0x91, 0x51, 0xb9, 0x36, 0xc8, 0x63, 0x89, 0xa8, 0x74, 0xd8, 0x2e, 0x8a, 0x3c, 0x79, 0x6f, | ||||||
| 	0xd7, 0x2c, 0x58, 0x24, 0xb9, 0x57, 0x84, 0x40, 0xb7, 0xa6, 0x2f, 0x98, 0xc6, 0xce, 0xd7, 0x7d, | 	0x39, 0xdb, 0x41, 0xe6, 0x0f, 0x89, 0x58, 0x86, 0x87, 0xd3, 0xc6, 0x2c, 0x88, 0x3c, 0x22, 0x04, | ||||||
| 	0x93, 0x7f, 0x90, 0x70, 0xf1, 0x2c, 0x8a, 0x46, 0xf1, 0xb4, 0xe7, 0x78, 0xcf, 0xea, 0x9d, 0xe2, | 	0xda, 0x29, 0x7b, 0xc6, 0xb0, 0xe3, 0xfa, 0xba, 0x6f, 0x72, 0x0e, 0x41, 0x92, 0x3d, 0x66, 0x71, | ||||||
| 	0xd9, 0x0d, 0x4c, 0x36, 0x0a, 0xa9, 0xc1, 0xe3, 0x22, 0x39, 0xbe, 0x92, 0x39, 0xc4, 0xa5, 0x13, | 	0xae, 0x92, 0xb0, 0xeb, 0xf8, 0xae, 0xc5, 0x0b, 0x95, 0xd0, 0xf7, 0x70, 0x34, 0x57, 0xc8, 0x0c, | ||||||
| 	0x6e, 0x9f, 0xc1, 0x7a, 0xb0, 0xb4, 0x7b, 0xfb, 0x77, 0xff, 0x94, 0x3d, 0xc2, 0xf4, 0xb4, 0x4f, | 	0x16, 0x46, 0x22, 0xfc, 0x49, 0x2e, 0xa1, 0xb3, 0x74, 0xc0, 0xf9, 0xe9, 0xdf, 0xf6, 0xaf, 0xad, | ||||||
| 	0x4b, 0x72, 0x05, 0x63, 0xca, 0x15, 0xd2, 0xea, 0xad, 0xc0, 0x03, 0xd3, 0x46, 0xbb, 0x01, 0x49, | 	0x6f, 0x7f, 0xee, 0x8f, 0xe8, 0x77, 0x38, 0xae, 0xde, 0xd3, 0x92, 0xbc, 0x81, 0x11, 0x4b, 0x14, | ||||||
| 	0x3e, 0xf2, 0xf4, 0xce, 0xc1, 0xd6, 0xfc, 0xf0, 0xe7, 0xf9, 0x97, 0x30, 0xd9, 0x22, 0xc7, 0x76, | 	0x32, 0xfe, 0x2b, 0xc6, 0x17, 0xa1, 0x8d, 0x76, 0x02, 0x41, 0x34, 0xf4, 0xec, 0x67, 0x47, 0x96, | ||||||
| 	0xae, 0xb3, 0x1b, 0x67, 0x2b, 0x98, 0x9e, 0x96, 0x68, 0x49, 0x2e, 0xa0, 0x5f, 0x0b, 0x53, 0x3c, | 	0xf4, 0x9b, 0xff, 0xd6, 0xbf, 0x80, 0xa3, 0x3b, 0x4c, 0xb0, 0xfc, 0xae, 0x5a, 0xc6, 0xf4, 0x06, | ||||||
| 	0x89, 0xa6, 0xae, 0xbc, 0x7b, 0x52, 0x0b, 0x73, 0x6f, 0xf5, 0xfa, 0x3d, 0x80, 0x68, 0x8b, 0x07, | 	0x8e, 0xab, 0x25, 0x5a, 0x92, 0xd7, 0xd0, 0x4b, 0x33, 0x13, 0xff, 0xc8, 0xf2, 0x94, 0xfb, 0xee, | ||||||
| 	0x72, 0x0b, 0xc3, 0x76, 0x76, 0xf2, 0xfb, 0x18, 0xe0, 0xf4, 0x0c, 0xff, 0xff, 0x7c, 0x43, 0xb5, | 	0x41, 0x9a, 0x99, 0x2f, 0x16, 0x53, 0x01, 0xc1, 0x3d, 0xd3, 0x7a, 0x95, 0x29, 0x4e, 0x4e, 0xe0, | ||||||
| 	0xcc, 0x3a, 0xb6, 0xbd, 0xed, 0xeb, 0xdb, 0xcf, 0xd2, 0xfa, 0xf6, 0xf3, 0x80, 0x59, 0x67, 0x1f, | 	0x10, 0x9f, 0x99, 0x48, 0xbc, 0x5e, 0x01, 0x6c, 0x78, 0x4f, 0x4c, 0x3f, 0xb9, 0x87, 0x0d, 0x22, | ||||||
| 	0xbb, 0x3f, 0xe8, 0xfa, 0x33, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x13, 0xbd, 0x1b, 0x52, 0x02, 0x00, | 	0xf7, 0x4d, 0x26, 0x10, 0xe4, 0x1a, 0x95, 0x0b, 0xb5, 0xe5, 0x8a, 0x37, 0x98, 0x9c, 0x41, 0xd7, | ||||||
| 	0x00, | 	0x7e, 0xc7, 0x82, 0x87, 0xed, 0x62, 0xce, 0x16, 0x7e, 0xe3, 0xf4, 0x13, 0x8c, 0x8b, 0x78, 0xd6, | ||||||
|  | 	0x0d, 0xad, 0x81, 0x2b, 0x08, 0xa4, 0x87, 0x3e, 0xda, 0xa1, 0xb3, 0xbe, 0xa9, 0xd9, 0x1c, 0xd3, | ||||||
|  | 	0x0f, 0x40, 0xea, 0xf7, 0xff, 0x3b, 0x60, 0xfa, 0x08, 0xe3, 0x85, 0xe4, 0xb5, 0xe6, 0xbb, 0x0d, | ||||||
|  | 	0x9f, 0x43, 0x90, 0xe2, 0x2a, 0x2e, 0x99, 0xee, 0xa6, 0xb8, 0xfa, 0x6a, 0x7d, 0x5f, 0xc0, 0xc0, | ||||||
|  | 	0x1e, 0xd5, 0xbc, 0xf7, 0x53, 0x5c, 0x2d, 0x3c, 0x45, 0xdf, 0x02, 0xa9, 0x37, 0xda, 0x37, 0x83, | ||||||
|  | 	0x2b, 0x18, 0x17, 0x43, 0xdb, 0xfb, 0x36, 0xab, 0x5e, 0x2f, 0xdd, 0xa3, 0x7e, 0xfb, 0xa7, 0x09, | ||||||
|  | 	0xad, 0x3b, 0x7c, 0x21, 0x1f, 0x61, 0x50, 0xde, 0x4e, 0x72, 0x52, 0xac, 0x58, 0x75, 0xd1, 0x27, | ||||||
|  | 	0xaf, 0x76, 0xb0, 0x5a, 0xd2, 0x03, 0x7b, 0xbd, 0xbc, 0x59, 0xfe, 0x7a, 0x6d, 0x1f, 0xfd, 0xf5, | ||||||
|  | 	0xfa, 0x0a, 0xd2, 0x03, 0x32, 0x87, 0x51, 0x75, 0x78, 0xe4, 0xb4, 0xd4, 0xa9, 0x64, 0x7c, 0x72, | ||||||
|  | 	0xb6, 0x93, 0x5f, 0x8b, 0x54, 0xb3, 0xf5, 0x22, 0x5b, 0x93, 0xf5, 0x22, 0xdb, 0x83, 0x28, 0x44, | ||||||
|  | 	0xaa, 0x11, 0x7a, 0x91, 0xad, 0x11, 0x78, 0x91, 0xed, 0xbc, 0xe9, 0xc1, 0x43, 0xc7, 0xfd, 0xf2, | ||||||
|  | 	0xde, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x63, 0xfb, 0xcd, 0x93, 0x03, 0x05, 0x00, 0x00, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,10 +37,59 @@ message DeleteClientResp { | |||||||
|  |  | ||||||
| // TODO(ericchiang): expand this. | // TODO(ericchiang): expand this. | ||||||
|  |  | ||||||
|  | // Password is an email for password mapping managed by the storage. | ||||||
|  | message Password { | ||||||
|  |   string email = 1; | ||||||
|  |  | ||||||
|  |   // Currently we do not accept plain text passwords. Could be an option in the future. | ||||||
|  |   bytes hash = 2; | ||||||
|  |   string username = 3; | ||||||
|  |   string user_id = 4; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreatePasswordReq is a request to make a password. | ||||||
|  | message CreatePasswordReq { | ||||||
|  |   Password password = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreatePasswordResp returns the response from creating a password. | ||||||
|  | message CreatePasswordResp { | ||||||
|  |   bool already_exists = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdatePasswordReq is a request to modify an existing password. | ||||||
|  | message UpdatePasswordReq { | ||||||
|  |   // The email used to lookup the password. This field cannot be modified | ||||||
|  |   string email = 1; | ||||||
|  |   bytes new_hash = 2; | ||||||
|  |   string new_username = 3; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdatePasswordResp returns the response from modifying an existing password.  | ||||||
|  | message UpdatePasswordResp { | ||||||
|  |   bool not_found = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeletePasswordReq is a request to delete a password. | ||||||
|  | message DeletePasswordReq { | ||||||
|  |   string email = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeletePasswordResp returns the response from deleting a password.  | ||||||
|  | message DeletePasswordResp { | ||||||
|  |   bool not_found = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Dex represents the dex gRPC service. | // Dex represents the dex gRPC service. | ||||||
| service Dex { | service Dex { | ||||||
|   // CreateClient attempts to create the client. |   // CreateClient attempts to create the client. | ||||||
|   rpc CreateClient(CreateClientReq) returns (CreateClientResp) {}; |   rpc CreateClient(CreateClientReq) returns (CreateClientResp) {}; | ||||||
|   // DeleteClient attempts to delete the provided client. |   // DeleteClient attempts to delete the provided client. | ||||||
|   rpc DeleteClient(DeleteClientReq) returns (DeleteClientResp) {}; |   rpc DeleteClient(DeleteClientReq) returns (DeleteClientResp) {}; | ||||||
|  |   // CreatePassword attempts to create the password. | ||||||
|  |   rpc CreatePassword(CreatePasswordReq) returns (CreatePasswordResp) {}; | ||||||
|  |   // UpdatePassword attempts to modify existing password. | ||||||
|  |   rpc UpdatePassword(UpdatePasswordReq) returns (UpdatePasswordResp) {}; | ||||||
|  |   // DeletePassword attempts to delete the password. | ||||||
|  |   rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {}; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								server/api.go
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								server/api.go
									
									
									
									
									
								
							| @@ -2,8 +2,10 @@ package server | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/crypto/bcrypt" | ||||||
| 	"golang.org/x/net/context" | 	"golang.org/x/net/context" | ||||||
|  |  | ||||||
| 	"github.com/coreos/dex/api" | 	"github.com/coreos/dex/api" | ||||||
| @@ -43,7 +45,7 @@ func (d dexAPI) CreateClient(ctx context.Context, req *api.CreateClientReq) (*ap | |||||||
| 	if err := d.s.CreateClient(c); err != nil { | 	if err := d.s.CreateClient(c); err != nil { | ||||||
| 		log.Printf("api: failed to create client: %v", err) | 		log.Printf("api: failed to create client: %v", err) | ||||||
| 		// TODO(ericchiang): Surface "already exists" errors. | 		// TODO(ericchiang): Surface "already exists" errors. | ||||||
| 		return nil, err | 		return nil, fmt.Errorf("create client: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &api.CreateClientResp{ | 	return &api.CreateClientResp{ | ||||||
| @@ -58,7 +60,102 @@ func (d dexAPI) DeleteClient(ctx context.Context, req *api.DeleteClientReq) (*ap | |||||||
| 			return &api.DeleteClientResp{NotFound: true}, nil | 			return &api.DeleteClientResp{NotFound: true}, nil | ||||||
| 		} | 		} | ||||||
| 		log.Printf("api: failed to delete client: %v", err) | 		log.Printf("api: failed to delete client: %v", err) | ||||||
| 		return nil, err | 		return nil, fmt.Errorf("delete client: %v", err) | ||||||
| 	} | 	} | ||||||
| 	return &api.DeleteClientResp{}, nil | 	return &api.DeleteClientResp{}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // checkCost returns an error if the hash provided does not meet minimum cost requirement | ||||||
|  | func checkCost(hash []byte) error { | ||||||
|  | 	actual, err := bcrypt.Cost(hash) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("parsing bcrypt hash: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if actual < bcrypt.DefaultCost { | ||||||
|  | 		return fmt.Errorf("given hash cost = %d, does not meet minimum cost requirement = %d", actual, bcrypt.DefaultCost) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d dexAPI) CreatePassword(ctx context.Context, req *api.CreatePasswordReq) (*api.CreatePasswordResp, error) { | ||||||
|  | 	if req.Password == nil { | ||||||
|  | 		return nil, errors.New("no password supplied") | ||||||
|  | 	} | ||||||
|  | 	if req.Password.UserId == "" { | ||||||
|  | 		return nil, errors.New("no user ID supplied") | ||||||
|  | 	} | ||||||
|  | 	if req.Password.Hash != nil { | ||||||
|  | 		if err := checkCost(req.Password.Hash); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		return nil, errors.New("no hash of password supplied") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	p := storage.Password{ | ||||||
|  | 		Email:    req.Password.Email, | ||||||
|  | 		Hash:     req.Password.Hash, | ||||||
|  | 		Username: req.Password.Username, | ||||||
|  | 		UserID:   req.Password.UserId, | ||||||
|  | 	} | ||||||
|  | 	if err := d.s.CreatePassword(p); err != nil { | ||||||
|  | 		log.Printf("api: failed to create password: %v", err) | ||||||
|  | 		return nil, fmt.Errorf("create password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &api.CreatePasswordResp{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d dexAPI) UpdatePassword(ctx context.Context, req *api.UpdatePasswordReq) (*api.UpdatePasswordResp, error) { | ||||||
|  | 	if req.Email == "" { | ||||||
|  | 		return nil, errors.New("no email supplied") | ||||||
|  | 	} | ||||||
|  | 	if req.NewHash == nil && req.NewUsername == "" { | ||||||
|  | 		return nil, errors.New("nothing to update") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if req.NewHash != nil { | ||||||
|  | 		if err := checkCost(req.NewHash); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	updater := func(old storage.Password) (storage.Password, error) { | ||||||
|  | 		if req.NewHash != nil { | ||||||
|  | 			old.Hash = req.NewHash | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if req.NewUsername != "" { | ||||||
|  | 			old.Username = req.NewUsername | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return old, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := d.s.UpdatePassword(req.Email, updater); err != nil { | ||||||
|  | 		if err == storage.ErrNotFound { | ||||||
|  | 			return &api.UpdatePasswordResp{NotFound: true}, nil | ||||||
|  | 		} | ||||||
|  | 		log.Printf("api: failed to update password: %v", err) | ||||||
|  | 		return nil, fmt.Errorf("update password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &api.UpdatePasswordResp{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d dexAPI) DeletePassword(ctx context.Context, req *api.DeletePasswordReq) (*api.DeletePasswordResp, error) { | ||||||
|  | 	if req.Email == "" { | ||||||
|  | 		return nil, errors.New("no email supplied") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err := d.s.DeletePassword(req.Email) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if err == storage.ErrNotFound { | ||||||
|  | 			return &api.DeletePasswordResp{NotFound: true}, nil | ||||||
|  | 		} | ||||||
|  | 		log.Printf("api: failed to delete password: %v", err) | ||||||
|  | 		return nil, fmt.Errorf("delete password: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return &api.DeletePasswordResp{}, nil | ||||||
|  |  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1 +1,59 @@ | |||||||
| package server | package server | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/coreos/dex/api" | ||||||
|  | 	"github.com/coreos/dex/storage/memory" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Attempts to create, update and delete a test Password | ||||||
|  | func TestPassword(t *testing.T) { | ||||||
|  | 	s := memory.New() | ||||||
|  | 	serv := NewAPI(s) | ||||||
|  |  | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	p := api.Password{ | ||||||
|  | 		Email: "test@example.com", | ||||||
|  | 		// bcrypt hash of the value "test1" with cost 10 | ||||||
|  | 		Hash:     []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"), | ||||||
|  | 		Username: "test", | ||||||
|  | 		UserId:   "test123", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	createReq := api.CreatePasswordReq{ | ||||||
|  | 		Password: &p, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := serv.CreatePassword(ctx, &createReq); err != nil { | ||||||
|  | 		t.Fatalf("Unable to create password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	updateReq := api.UpdatePasswordReq{ | ||||||
|  | 		Email:       "test@example.com", | ||||||
|  | 		NewUsername: "test1", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := serv.UpdatePassword(ctx, &updateReq); err != nil { | ||||||
|  | 		t.Fatalf("Unable to update password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pass, err := s.GetPassword(updateReq.Email) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Unable to retrieve password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if pass.Username != updateReq.NewUsername { | ||||||
|  | 		t.Fatalf("UpdatePassword failed. Expected username %s retrieved %s", updateReq.NewUsername, pass.Username) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	deleteReq := api.DeletePasswordReq{ | ||||||
|  | 		Email: "test@example.com", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := serv.DeletePassword(ctx, &deleteReq); err != nil { | ||||||
|  | 		t.Fatalf("Unable to delete password: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -238,8 +238,7 @@ type Password struct { | |||||||
| 	// (cough cough, kubernetes), must map this value appropriately. | 	// (cough cough, kubernetes), must map this value appropriately. | ||||||
| 	Email string `yaml:"email"` | 	Email string `yaml:"email"` | ||||||
|  |  | ||||||
| 	// Bcrypt encoded hash of the password. This package recommends a cost value of at | 	// Bcrypt encoded hash of the password. This package enforces a min cost value of 10 | ||||||
| 	// least 14. |  | ||||||
| 	Hash []byte `yaml:"hash"` | 	Hash []byte `yaml:"hash"` | ||||||
|  |  | ||||||
| 	// Optional username to display. NOT used during login. | 	// Optional username to display. NOT used during login. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user