Implement the Auth interface, with JWT and service implementations.

* Update Auth Interface

* Define Auth Service Implementation

* Support Service Auth

* Add Auth Service Proto

* Remove erronious files

* Implement Auth Service Package

* Update Auth Interface

* Update Auth Interface. Add Validate, remove Add/Remove roles

* Make Revoke interface more explicit

* Refactor serializing and deserializing service accounts

* Fix srv name & update interface to be more explicit

* Require jwt public key for auth

* Rename Variables (Resource.ID => Resource.Name & ServiceAccount => Account)

* Implement JWT Auth Package

* Remove parent, add ID

* Update auth imports to v2. Add String() to auth interface
This commit is contained in:
ben-toogood 2020-02-03 08:16:02 +00:00 committed by GitHub
parent 449bcb46fe
commit d621548120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1103 additions and 26 deletions

View File

@ -7,34 +7,44 @@ import (
// Auth providers authentication and authorization
type Auth interface {
// Generate a new auth token
Generate(string) (*Token, error)
// Revoke an authorization token
Revoke(*Token) error
// Grant access to a resource
Grant(*Token, *Service) error
// Verify a token can access a resource
Verify(*Token, *Service) error
// String to identify the package
String() string
// Init the auth package
Init(opts ...Option) error
// Generate a new auth Account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Revoke an authorization Account
Revoke(token string) error
// Validate an account token
Validate(token string) (*Account, error)
}
// Service is some thing to provide access to
type Service struct {
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource
Name string
// Endpoint is the specific endpoint
Endpoint string
// Type of resource, e.g.
Type string
}
// Token providers by an auth provider
type Token struct {
// Unique token id
// Role an account has
type Role struct {
Name string
Resource *Resource
}
// Account provided by an auth provider
type Account struct {
// ID of the account (UUID or email)
Id string `json: "id"`
// Time of token creation
// Token used to authenticate
Token string `json: "token"`
// Time of Account creation
Created time.Time `json:"created"`
// Time of token expiry
// Time of Account expiry
Expiry time.Time `json:"expiry"`
// Roles associated with the token
Roles []string `json:"roles"`
// Roles associated with the Account
Roles []*Role `json:"roles"`
// Any other associated metadata
Metadata map[string]string `json:"metadata"`
}

34
auth/default.go Normal file
View File

@ -0,0 +1,34 @@
package auth
var (
DefaultAuth Auth = new(noop)
)
type noop struct {
options Options
}
// String name of implementation
func (a *noop) String() string {
return "noop"
}
// Init the svc
func (a *noop) Init(...Option) error {
return nil
}
// Generate a new auth Account
func (a *noop) Generate(id string, ops ...GenerateOption) (*Account, error) {
return nil, nil
}
// Revoke an authorization Account
func (a *noop) Revoke(token string) error {
return nil
}
// Validate a account token
func (a *noop) Validate(token string) (*Account, error) {
return nil, nil
}

106
auth/jwt/jwt.go Normal file
View File

@ -0,0 +1,106 @@
package jwt
import (
"errors"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/micro/go-micro/v2/auth"
)
// ErrInvalidPrivateKey is returned when the service provided an invalid private key
var ErrInvalidPrivateKey = errors.New("An invalid private key was provided")
// ErrEncodingToken is returned when the service encounters an error during encoding
var ErrEncodingToken = errors.New("An error occured while encoding the JWT")
// ErrInvalidToken is returned when the token provided is not valid
var ErrInvalidToken = errors.New("An invalid token was provided")
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
svc := new(svc)
svc.Init(opts...)
return svc
}
// svc is the JWT implementation of the Auth interface
type svc struct {
options auth.Options
}
func (s *svc) String() string {
return "jwt"
}
func (s *svc) Init(opts ...auth.Option) error {
for _, o := range opts {
o(&s.options)
}
return nil
}
// AuthClaims to be encoded in the JWT
type AuthClaims struct {
Id string `json:"id"`
Roles []*auth.Role `json:"roles"`
Metadata map[string]string `json:"metadata"`
jwt.StandardClaims
}
// Generate a new JWT
func (s *svc) Generate(id string, ops ...auth.GenerateOption) (*auth.Account, error) {
key, err := jwt.ParseRSAPrivateKeyFromPEM(s.options.PrivateKey)
if err != nil {
return nil, ErrEncodingToken
}
options := auth.NewGenerateOptions(ops...)
account := jwt.NewWithClaims(jwt.SigningMethodRS256, AuthClaims{
id, options.Roles, options.Metadata, jwt.StandardClaims{
Subject: "TODO",
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
},
})
token, err := account.SignedString(key)
if err != nil {
return nil, err
}
return &auth.Account{
Id: id,
Token: token,
Roles: options.Roles,
Metadata: options.Metadata,
}, nil
}
// Revoke an authorization account
func (s *svc) Revoke(token string) error {
return nil
}
// Validate a JWT
func (s *svc) Validate(token string) (*auth.Account, error) {
res, err := jwt.ParseWithClaims(token, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(s.options.PublicKey)
})
if err != nil {
return nil, err
}
if !res.Valid {
return nil, ErrInvalidToken
}
claims := res.Claims.(*AuthClaims)
return &auth.Account{
Id: claims.Id,
Metadata: claims.Metadata,
Roles: claims.Roles,
}, nil
}

57
auth/options.go Normal file
View File

@ -0,0 +1,57 @@
package auth
import (
b64 "encoding/base64"
)
type Options struct {
PublicKey []byte
PrivateKey []byte
}
type Option func(o *Options)
// PublicKey is the JWT public key
func PublicKey(key string) Option {
return func(o *Options) {
o.PublicKey, _ = b64.StdEncoding.DecodeString(key)
}
}
// PrivateKey is the JWT private key
func PrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey, _ = b64.StdEncoding.DecodeString(key)
}
}
type GenerateOptions struct {
Metadata map[string]string
Roles []*Role
}
type GenerateOption func(o *GenerateOptions)
// Metadata for the generated account
func Metadata(md map[string]string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// Roles for the generated account
func Roles(rs []*Role) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
return options
}

View File

@ -0,0 +1,466 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: auth/service/proto/auth.proto
package go_micro_auth
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Account struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"`
Expiry int64 `protobuf:"varint,4,opt,name=expiry,proto3" json:"expiry,omitempty"`
Roles []*Role `protobuf:"bytes,5,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Account) Reset() { *m = Account{} }
func (m *Account) String() string { return proto.CompactTextString(m) }
func (*Account) ProtoMessage() {}
func (*Account) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{0}
}
func (m *Account) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Account.Unmarshal(m, b)
}
func (m *Account) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Account.Marshal(b, m, deterministic)
}
func (m *Account) XXX_Merge(src proto.Message) {
xxx_messageInfo_Account.Merge(m, src)
}
func (m *Account) XXX_Size() int {
return xxx_messageInfo_Account.Size(m)
}
func (m *Account) XXX_DiscardUnknown() {
xxx_messageInfo_Account.DiscardUnknown(m)
}
var xxx_messageInfo_Account proto.InternalMessageInfo
func (m *Account) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Account) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
func (m *Account) GetCreated() int64 {
if m != nil {
return m.Created
}
return 0
}
func (m *Account) GetExpiry() int64 {
if m != nil {
return m.Expiry
}
return 0
}
func (m *Account) GetRoles() []*Role {
if m != nil {
return m.Roles
}
return nil
}
func (m *Account) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
type Role struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Role) Reset() { *m = Role{} }
func (m *Role) String() string { return proto.CompactTextString(m) }
func (*Role) ProtoMessage() {}
func (*Role) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{1}
}
func (m *Role) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Role.Unmarshal(m, b)
}
func (m *Role) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Role.Marshal(b, m, deterministic)
}
func (m *Role) XXX_Merge(src proto.Message) {
xxx_messageInfo_Role.Merge(m, src)
}
func (m *Role) XXX_Size() int {
return xxx_messageInfo_Role.Size(m)
}
func (m *Role) XXX_DiscardUnknown() {
xxx_messageInfo_Role.DiscardUnknown(m)
}
var xxx_messageInfo_Role proto.InternalMessageInfo
func (m *Role) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Role) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
type Resource struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Resource) Reset() { *m = Resource{} }
func (m *Resource) String() string { return proto.CompactTextString(m) }
func (*Resource) ProtoMessage() {}
func (*Resource) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{2}
}
func (m *Resource) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Resource.Unmarshal(m, b)
}
func (m *Resource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Resource.Marshal(b, m, deterministic)
}
func (m *Resource) XXX_Merge(src proto.Message) {
xxx_messageInfo_Resource.Merge(m, src)
}
func (m *Resource) XXX_Size() int {
return xxx_messageInfo_Resource.Size(m)
}
func (m *Resource) XXX_DiscardUnknown() {
xxx_messageInfo_Resource.DiscardUnknown(m)
}
var xxx_messageInfo_Resource proto.InternalMessageInfo
func (m *Resource) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Resource) GetType() string {
if m != nil {
return m.Type
}
return ""
}
type GenerateRequest struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GenerateRequest) Reset() { *m = GenerateRequest{} }
func (m *GenerateRequest) String() string { return proto.CompactTextString(m) }
func (*GenerateRequest) ProtoMessage() {}
func (*GenerateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{3}
}
func (m *GenerateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenerateRequest.Unmarshal(m, b)
}
func (m *GenerateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GenerateRequest.Marshal(b, m, deterministic)
}
func (m *GenerateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GenerateRequest.Merge(m, src)
}
func (m *GenerateRequest) XXX_Size() int {
return xxx_messageInfo_GenerateRequest.Size(m)
}
func (m *GenerateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GenerateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GenerateRequest proto.InternalMessageInfo
func (m *GenerateRequest) GetAccount() *Account {
if m != nil {
return m.Account
}
return nil
}
type GenerateResponse struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GenerateResponse) Reset() { *m = GenerateResponse{} }
func (m *GenerateResponse) String() string { return proto.CompactTextString(m) }
func (*GenerateResponse) ProtoMessage() {}
func (*GenerateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{4}
}
func (m *GenerateResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenerateResponse.Unmarshal(m, b)
}
func (m *GenerateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GenerateResponse.Marshal(b, m, deterministic)
}
func (m *GenerateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GenerateResponse.Merge(m, src)
}
func (m *GenerateResponse) XXX_Size() int {
return xxx_messageInfo_GenerateResponse.Size(m)
}
func (m *GenerateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GenerateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GenerateResponse proto.InternalMessageInfo
func (m *GenerateResponse) GetAccount() *Account {
if m != nil {
return m.Account
}
return nil
}
type ValidateRequest struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ValidateRequest) Reset() { *m = ValidateRequest{} }
func (m *ValidateRequest) String() string { return proto.CompactTextString(m) }
func (*ValidateRequest) ProtoMessage() {}
func (*ValidateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{5}
}
func (m *ValidateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ValidateRequest.Unmarshal(m, b)
}
func (m *ValidateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ValidateRequest.Marshal(b, m, deterministic)
}
func (m *ValidateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ValidateRequest.Merge(m, src)
}
func (m *ValidateRequest) XXX_Size() int {
return xxx_messageInfo_ValidateRequest.Size(m)
}
func (m *ValidateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ValidateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ValidateRequest proto.InternalMessageInfo
func (m *ValidateRequest) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
type ValidateResponse struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ValidateResponse) Reset() { *m = ValidateResponse{} }
func (m *ValidateResponse) String() string { return proto.CompactTextString(m) }
func (*ValidateResponse) ProtoMessage() {}
func (*ValidateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{6}
}
func (m *ValidateResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ValidateResponse.Unmarshal(m, b)
}
func (m *ValidateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ValidateResponse.Marshal(b, m, deterministic)
}
func (m *ValidateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ValidateResponse.Merge(m, src)
}
func (m *ValidateResponse) XXX_Size() int {
return xxx_messageInfo_ValidateResponse.Size(m)
}
func (m *ValidateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ValidateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ValidateResponse proto.InternalMessageInfo
func (m *ValidateResponse) GetAccount() *Account {
if m != nil {
return m.Account
}
return nil
}
type RevokeRequest struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RevokeRequest) Reset() { *m = RevokeRequest{} }
func (m *RevokeRequest) String() string { return proto.CompactTextString(m) }
func (*RevokeRequest) ProtoMessage() {}
func (*RevokeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{7}
}
func (m *RevokeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RevokeRequest.Unmarshal(m, b)
}
func (m *RevokeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RevokeRequest.Marshal(b, m, deterministic)
}
func (m *RevokeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RevokeRequest.Merge(m, src)
}
func (m *RevokeRequest) XXX_Size() int {
return xxx_messageInfo_RevokeRequest.Size(m)
}
func (m *RevokeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RevokeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RevokeRequest proto.InternalMessageInfo
func (m *RevokeRequest) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
type RevokeResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RevokeResponse) Reset() { *m = RevokeResponse{} }
func (m *RevokeResponse) String() string { return proto.CompactTextString(m) }
func (*RevokeResponse) ProtoMessage() {}
func (*RevokeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{8}
}
func (m *RevokeResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RevokeResponse.Unmarshal(m, b)
}
func (m *RevokeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RevokeResponse.Marshal(b, m, deterministic)
}
func (m *RevokeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RevokeResponse.Merge(m, src)
}
func (m *RevokeResponse) XXX_Size() int {
return xxx_messageInfo_RevokeResponse.Size(m)
}
func (m *RevokeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RevokeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RevokeResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Account)(nil), "go.micro.auth.Account")
proto.RegisterMapType((map[string]string)(nil), "go.micro.auth.Account.MetadataEntry")
proto.RegisterType((*Role)(nil), "go.micro.auth.Role")
proto.RegisterType((*Resource)(nil), "go.micro.auth.Resource")
proto.RegisterType((*GenerateRequest)(nil), "go.micro.auth.GenerateRequest")
proto.RegisterType((*GenerateResponse)(nil), "go.micro.auth.GenerateResponse")
proto.RegisterType((*ValidateRequest)(nil), "go.micro.auth.ValidateRequest")
proto.RegisterType((*ValidateResponse)(nil), "go.micro.auth.ValidateResponse")
proto.RegisterType((*RevokeRequest)(nil), "go.micro.auth.RevokeRequest")
proto.RegisterType((*RevokeResponse)(nil), "go.micro.auth.RevokeResponse")
}
func init() { proto.RegisterFile("auth/service/proto/auth.proto", fileDescriptor_21300bfacc51fc2a) }
var fileDescriptor_21300bfacc51fc2a = []byte{
// 429 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xad, 0x3f, 0xe2, 0x98, 0x89, 0xd2, 0x46, 0x03, 0x2a, 0x56, 0x44, 0x21, 0xb2, 0x40, 0x84,
0x8b, 0x83, 0xdc, 0x0b, 0x82, 0x0b, 0x15, 0xa0, 0x9e, 0x2a, 0xa4, 0x3d, 0x70, 0x5f, 0xec, 0x11,
0xb5, 0xe2, 0x78, 0xcd, 0x7a, 0x1d, 0xe1, 0xdf, 0xc0, 0x6f, 0xe5, 0x3f, 0x20, 0xaf, 0xbd, 0x69,
0xea, 0xb4, 0xaa, 0xd4, 0xdb, 0x7c, 0xbc, 0x79, 0xf3, 0xde, 0x68, 0x17, 0xce, 0x78, 0xad, 0xae,
0x57, 0x15, 0xc9, 0x6d, 0x96, 0xd0, 0xaa, 0x94, 0x42, 0x89, 0x55, 0x5b, 0x8a, 0x74, 0x88, 0xd3,
0x5f, 0x22, 0xda, 0x64, 0x89, 0x14, 0x51, 0x5b, 0x0c, 0xff, 0xda, 0x30, 0xbe, 0x48, 0x12, 0x51,
0x17, 0x0a, 0x8f, 0xc1, 0xce, 0xd2, 0xc0, 0x5a, 0x58, 0xcb, 0x27, 0xcc, 0xce, 0x52, 0x7c, 0x06,
0x23, 0x25, 0xd6, 0x54, 0x04, 0xb6, 0x2e, 0x75, 0x09, 0x06, 0x30, 0x4e, 0x24, 0x71, 0x45, 0x69,
0xe0, 0x2c, 0xac, 0xa5, 0xc3, 0x4c, 0x8a, 0xa7, 0xe0, 0xd1, 0x9f, 0x32, 0x93, 0x4d, 0xe0, 0xea,
0x46, 0x9f, 0xe1, 0x3b, 0x18, 0x49, 0x91, 0x53, 0x15, 0x8c, 0x16, 0xce, 0x72, 0x12, 0x3f, 0x8d,
0x6e, 0x49, 0x88, 0x98, 0xc8, 0x89, 0x75, 0x08, 0xfc, 0x0c, 0xfe, 0x86, 0x14, 0x4f, 0xb9, 0xe2,
0x81, 0xa7, 0xd1, 0xaf, 0x07, 0xe8, 0x5e, 0x6c, 0x74, 0xd5, 0xc3, 0xbe, 0x15, 0x4a, 0x36, 0x6c,
0x37, 0x35, 0xff, 0x04, 0xd3, 0x5b, 0x2d, 0x9c, 0x81, 0xb3, 0xa6, 0xa6, 0xb7, 0xd5, 0x86, 0xad,
0xaf, 0x2d, 0xcf, 0x6b, 0x32, 0xbe, 0x74, 0xf2, 0xd1, 0xfe, 0x60, 0x85, 0xdf, 0xc1, 0x6d, 0xd5,
0x20, 0x82, 0x5b, 0xf0, 0x0d, 0xf5, 0x43, 0x3a, 0xc6, 0x73, 0xf0, 0x25, 0x55, 0xa2, 0x96, 0x49,
0x37, 0x38, 0x89, 0x9f, 0x0f, 0x8d, 0xf4, 0x6d, 0xb6, 0x03, 0x86, 0x31, 0xf8, 0xa6, 0x7a, 0x27,
0x29, 0x82, 0xab, 0x9a, 0xd2, 0x28, 0xd1, 0x71, 0xf8, 0x05, 0x4e, 0x2e, 0xa9, 0x20, 0xc9, 0x15,
0x31, 0xfa, 0x5d, 0x53, 0xa5, 0xf0, 0x3d, 0x8c, 0x79, 0xe7, 0x5b, 0x4f, 0x4f, 0xe2, 0xd3, 0xbb,
0xaf, 0xc2, 0x0c, 0x2c, 0xfc, 0x0a, 0xb3, 0x1b, 0x92, 0xaa, 0x14, 0x45, 0x45, 0x8f, 0x60, 0x79,
0x0b, 0x27, 0x3f, 0x78, 0x9e, 0xa5, 0x7b, 0x52, 0x76, 0x8f, 0xc2, 0xda, 0x7b, 0x14, 0xed, 0xba,
0x1b, 0xe0, 0xa3, 0xd7, 0xbd, 0x81, 0x29, 0xa3, 0xad, 0x58, 0x3f, 0xb0, 0x6c, 0x06, 0xc7, 0x06,
0xd6, 0xad, 0x8a, 0xff, 0x59, 0xe0, 0x5e, 0xd4, 0xea, 0x1a, 0xaf, 0xc0, 0x37, 0xb6, 0xf1, 0xe5,
0x60, 0xdd, 0xe0, 0xa8, 0xf3, 0x57, 0xf7, 0xf6, 0x3b, 0xd6, 0xf0, 0xa8, 0xa5, 0x33, 0xb6, 0x0e,
0xe8, 0x06, 0x87, 0x39, 0xa0, 0x1b, 0xde, 0x23, 0x3c, 0xc2, 0x4b, 0xf0, 0x3a, 0xe1, 0xf8, 0xe2,
0xe0, 0xe9, 0xec, 0xd9, 0x9e, 0x9f, 0xdd, 0xd3, 0x35, 0x44, 0x3f, 0x3d, 0xfd, 0x97, 0xcf, 0xff,
0x07, 0x00, 0x00, 0xff, 0xff, 0x79, 0x35, 0xb2, 0x7e, 0xec, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,125 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: auth/service/proto/auth.proto
package go_micro_auth
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Auth service
type AuthService interface {
Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error)
Validate(ctx context.Context, in *ValidateRequest, opts ...client.CallOption) (*ValidateResponse, error)
Revoke(ctx context.Context, in *RevokeRequest, opts ...client.CallOption) (*RevokeResponse, error)
}
type authService struct {
c client.Client
name string
}
func NewAuthService(name string, c client.Client) AuthService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.auth"
}
return &authService{
c: c,
name: name,
}
}
func (c *authService) Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Generate", in)
out := new(GenerateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Validate(ctx context.Context, in *ValidateRequest, opts ...client.CallOption) (*ValidateResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Validate", in)
out := new(ValidateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Revoke(ctx context.Context, in *RevokeRequest, opts ...client.CallOption) (*RevokeResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Revoke", in)
out := new(RevokeResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Auth service
type AuthHandler interface {
Generate(context.Context, *GenerateRequest, *GenerateResponse) error
Validate(context.Context, *ValidateRequest, *ValidateResponse) error
Revoke(context.Context, *RevokeRequest, *RevokeResponse) error
}
func RegisterAuthHandler(s server.Server, hdlr AuthHandler, opts ...server.HandlerOption) error {
type auth interface {
Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error
Validate(ctx context.Context, in *ValidateRequest, out *ValidateResponse) error
Revoke(ctx context.Context, in *RevokeRequest, out *RevokeResponse) error
}
type Auth struct {
auth
}
h := &authHandler{hdlr}
return s.Handle(s.NewHandler(&Auth{h}, opts...))
}
type authHandler struct {
AuthHandler
}
func (h *authHandler) Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error {
return h.AuthHandler.Generate(ctx, in, out)
}
func (h *authHandler) Validate(ctx context.Context, in *ValidateRequest, out *ValidateResponse) error {
return h.AuthHandler.Validate(ctx, in, out)
}
func (h *authHandler) Revoke(ctx context.Context, in *RevokeRequest, out *RevokeResponse) error {
return h.AuthHandler.Revoke(ctx, in, out)
}

View File

@ -0,0 +1,50 @@
syntax = "proto3";
package go.micro.auth;
service Auth {
rpc Generate(GenerateRequest) returns (GenerateResponse) {};
rpc Validate(ValidateRequest) returns (ValidateResponse) {};
rpc Revoke(RevokeRequest) returns (RevokeResponse) {};
}
message Account{
string id = 1;
string token = 2;
int64 created = 3;
int64 expiry = 4;
repeated Role roles = 5;
map<string, string> metadata = 6;
}
message Role {
string name = 1;
Resource resource = 2;
}
message Resource{
string name = 1;
string type = 2;
}
message GenerateRequest {
Account account = 1;
}
message GenerateResponse {
Account account = 1;
}
message ValidateRequest {
string token = 1;
}
message ValidateResponse {
Account account = 1;
}
message RevokeRequest {
string token = 1;
}
message RevokeResponse {}

128
auth/service/service.go Normal file
View File

@ -0,0 +1,128 @@
package service
import (
"context"
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/v2/auth"
pb "github.com/micro/go-micro/v2/auth/service/proto"
)
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
svc := new(svc)
svc.Init(opts...)
return svc
}
// svc is the service implementation of the Auth interface
type svc struct {
options auth.Options
auth pb.AuthService
}
func (s *svc) String() string {
return "service"
}
func (s *svc) Init(opts ...auth.Option) error {
for _, o := range opts {
o(&s.options)
}
dc := client.DefaultClient
s.auth = pb.NewAuthService("go.micro.auth", dc)
return nil
}
// Generate a new auth account
func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
// construct the request
options := auth.NewGenerateOptions(opts...)
sa := &auth.Account{
Id: id,
Roles: options.Roles,
Metadata: options.Metadata,
}
req := &pb.GenerateRequest{Account: serializeAccount(sa)}
// execute the request
resp, err := s.auth.Generate(context.Background(), req)
if err != nil {
return nil, err
}
// format the response
return deserializeAccount(resp.Account), nil
}
// Revoke an authorization account
func (s *svc) Revoke(token string) error {
// contruct the request
req := &pb.RevokeRequest{Token: token}
// execute the request
_, err := s.auth.Revoke(context.Background(), req)
return err
}
// Validate an account token
func (s *svc) Validate(token string) (*auth.Account, error) {
resp, err := s.auth.Validate(context.Background(), &pb.ValidateRequest{Token: token})
if err != nil {
return nil, err
}
return deserializeAccount(resp.Account), nil
}
func serializeAccount(sa *auth.Account) *pb.Account {
roles := make([]*pb.Role, len(sa.Roles))
for i, r := range sa.Roles {
roles[i] = &pb.Role{
Name: r.Name,
}
if r.Resource != nil {
roles[i].Resource = &pb.Resource{
Name: r.Resource.Name,
Type: r.Resource.Type,
}
}
}
return &pb.Account{
Id: sa.Id,
Roles: roles,
Metadata: sa.Metadata,
}
}
func deserializeAccount(a *pb.Account) *auth.Account {
// format the response
sa := &auth.Account{
Id: a.Id,
Token: a.Token,
Created: time.Unix(a.Created, 0),
Expiry: time.Unix(a.Expiry, 0),
Metadata: a.Metadata,
}
sa.Roles = make([]*auth.Role, len(a.Roles))
for i, r := range a.Roles {
sa.Roles[i] = &auth.Role{
Name: r.Name,
}
if r.Resource != nil {
sa.Roles[i].Resource = &auth.Resource{
Name: r.Resource.Name,
Type: r.Resource.Type,
}
}
}
return sa
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
@ -55,6 +56,10 @@ import (
// tracers
// jTracer "github.com/micro/go-micro/v2/debug/trace/jaeger"
memTracer "github.com/micro/go-micro/v2/debug/trace/memory"
// auth
jwtAuth "github.com/micro/go-micro/v2/auth/jwt"
sAuth "github.com/micro/go-micro/v2/auth/service"
)
type Cmd interface {
@ -223,6 +228,21 @@ var (
EnvVars: []string{"MICRO_TRACER_ADDRESS"},
Usage: "Comma-separated list of tracer addresses",
},
&cli.StringFlag{
Name: "auth",
EnvVars: []string{"MICRO_AUTH"},
Usage: "Auth for role based access control, e.g. service",
},
&cli.StringFlag{
Name: "auth_public_key",
EnvVars: []string{"MICRO_AUTH_PUBLIC_KEY"},
Usage: "Public key for JWT auth (base64 encoded PEM)",
},
&cli.StringFlag{
Name: "auth_private_key",
EnvVars: []string{"MICRO_AUTH_PRIVATE_KEY"},
Usage: "Private key for JWT auth (base64 encoded PEM)",
},
}
DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
@ -274,6 +294,11 @@ var (
// "jaeger": jTracer.NewTracer,
}
DefaultAuths = map[string]func(...auth.Option) auth.Auth{
"service": sAuth.NewAuth,
"jwt": jwtAuth.NewAuth,
}
// used for default selection as the fall back
defaultClient = "grpc"
defaultServer = "grpc"
@ -300,6 +325,7 @@ func newCmd(opts ...Option) Cmd {
Runtime: &runtime.DefaultRuntime,
Store: &store.DefaultStore,
Tracer: &trace.DefaultTracer,
Auth: &auth.DefaultAuth,
Brokers: DefaultBrokers,
Clients: DefaultClients,
@ -310,6 +336,7 @@ func newCmd(opts ...Option) Cmd {
Runtimes: DefaultRuntimes,
Stores: DefaultStores,
Tracers: DefaultTracers,
Auths: DefaultAuths,
}
for _, o := range opts {
@ -382,6 +409,16 @@ func (c *cmd) Before(ctx *cli.Context) error {
*c.opts.Tracer = r()
}
// Set the auth
if name := ctx.String("auth"); len(name) > 0 {
r, ok := c.opts.Auths[name]
if !ok {
return fmt.Errorf("Unsupported auth: %s", name)
}
*c.opts.Auth = r()
}
// Set the client
if name := ctx.String("client"); len(name) > 0 {
// only change if we have the client and type differs
@ -531,6 +568,18 @@ func (c *cmd) Before(ctx *cli.Context) error {
serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
}
if len(ctx.String("auth_public_key")) > 0 {
if err := (*c.opts.Auth).Init(auth.PublicKey(ctx.String("auth_public_key"))); err != nil {
log.Fatalf("Error configuring auth: %v", err)
}
}
if len(ctx.String("auth_private_key")) > 0 {
if err := (*c.opts.Auth).Init(auth.PrivateKey(ctx.String("auth_private_key"))); err != nil {
log.Fatalf("Error configuring auth: %v", err)
}
}
// client opts
if r := ctx.Int("client_retries"); r >= 0 {
clientOpts = append(clientOpts, client.Retries(r))

View File

@ -3,6 +3,7 @@ package cmd
import (
"context"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
@ -30,6 +31,7 @@ type Options struct {
Runtime *runtime.Runtime
Store *store.Store
Tracer *trace.Tracer
Auth *auth.Auth
Brokers map[string]func(...broker.Option) broker.Broker
Clients map[string]func(...client.Option) client.Client
@ -40,6 +42,7 @@ type Options struct {
Runtimes map[string]func(...runtime.Option) runtime.Runtime
Stores map[string]func(...store.Option) store.Store
Tracers map[string]func(...trace.Option) trace.Tracer
Auths map[string]func(...auth.Option) auth.Auth
// Other options for implementations of the interface
// can be stored in a context
@ -109,6 +112,12 @@ func Tracer(t *trace.Tracer) Option {
}
}
func Auth(a *auth.Auth) Option {
return func(o *Options) {
o.Auth = a
}
}
// New broker func
func NewBroker(name string, b func(...broker.Option) broker.Broker) Option {
return func(o *Options) {
@ -164,3 +173,10 @@ func NewTracer(name string, t func(...trace.Option) trace.Tracer) Option {
o.Tracers[name] = t
}
}
// New auth func
func NewAuth(name string, t func(...auth.Option) auth.Auth) Option {
return func(o *Options) {
o.Auths[name] = t
}
}

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/bitly/go-simplejson v0.5.0
github.com/bwmarrin/discordgo v0.20.2
github.com/coreos/etcd v3.3.18+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
github.com/fsnotify/fsnotify v1.4.7
github.com/fsouza/go-dockerclient v1.6.0

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
@ -120,6 +121,13 @@ func Tracer(t trace.Tracer) Option {
}
}
// Auth sets the auth for the service
func Auth(a auth.Auth) Option {
return func(o *Options) {
o.Server.Init(server.Auth(a))
}
}
// Selector sets the selector for the service client
func Selector(s selector.Selector) Option {
return func(o *Options) {

View File

@ -5,6 +5,7 @@ import (
"sync"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/debug/trace"
@ -17,6 +18,7 @@ type Options struct {
Broker broker.Broker
Registry registry.Registry
Tracer trace.Tracer
Auth auth.Auth
Transport transport.Transport
Metadata map[string]string
Name string
@ -161,6 +163,13 @@ func Tracer(t trace.Tracer) Option {
}
}
// Auth mechanism for role based access control
func Auth(a auth.Auth) Option {
return func(o *Options) {
o.Auth = a
}
}
// Transport mechanism for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
return func(o *Options) {

View File

@ -68,19 +68,26 @@ func (m *memoryStore) Read(key string, opts ...store.ReadOption) ([]*store.Recor
var vals []*memoryRecord
if !options.Prefix {
v, ok := m.values[key]
if !ok {
return nil, store.ErrNotFound
}
vals = []*memoryRecord{v}
} else {
if options.Prefix {
for _, v := range m.values {
if !strings.HasPrefix(v.r.Key, key) {
continue
}
vals = append(vals, v)
}
} else if options.Suffix {
for _, v := range m.values {
if !strings.HasSuffix(v.r.Key, key) {
continue
}
vals = append(vals, v)
}
} else {
v, ok := m.values[key]
if !ok {
return nil, store.ErrNotFound
}
vals = []*memoryRecord{v}
}
//nolint:prealloc

View File

@ -11,6 +11,8 @@ type Options struct {
Namespace string
// Prefix of the keys used
Prefix string
// Suffix of the keys used
Suffix string
// Alternative options
Context context.Context
}
@ -45,3 +47,10 @@ func ReadPrefix() ReadOption {
o.Prefix = true
}
}
// ReadSuffix uses the key as a suffix
func ReadSuffix() ReadOption {
return func(o *ReadOptions) {
o.Suffix = true
}
}

View File

@ -39,6 +39,8 @@ type Record struct {
type ReadOptions struct {
// Read key as a prefix
Prefix bool
// Read key as a suffix
Suffix bool
}
type ReadOption func(o *ReadOptions)