diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c883d49 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 0000000..4aabca4 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/zirconium-go.iml b/.idea/zirconium-go.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/zirconium-go.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a0ed704 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 ChronosX88 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 8675d44..d330b60 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ # Zirconium -**Zirconium** is a Cadmium protocol server experimental implementation +Zirconium is experimental serverside implementation of Cadmium protocol in Go. + +## Contributing + +Feel free to open PRs. If you found the bug, please let us know at our [bug tracker](https://github.com/cadmium-im/zirconium-go/issues). + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/cmd/zr/main.go b/cmd/zr/main.go index 56290e7..083c5c3 100644 --- a/cmd/zr/main.go +++ b/cmd/zr/main.go @@ -5,34 +5,22 @@ import ( "flag" "fmt" "io/ioutil" - "log" - "net/http" "os" - core "github.com/ChronosX88/zirconium/core" + "github.com/cadmium-im/zirconium-go/core" "github.com/google/logger" - "github.com/gorilla/mux" - "github.com/gorilla/websocket" "github.com/pelletier/go-toml" ) -var connectionHandler = core.NewConnectionHandler() -var upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - func main() { - - var cfg core.ServerConfig + var cfg core.Config var configPath string var generateConfig bool flag.StringVar(&configPath, "config", "", "Path to config") flag.BoolVar(&generateConfig, "gen_config", false, "Generate the config") flag.Parse() if generateConfig == true { - sampleConfig := &core.ServerConfig{} + sampleConfig := &core.Config{} val, err := toml.Marshal(sampleConfig) if err != nil { logger.Errorf("Failed to generate config: %s", err.Error()) @@ -61,30 +49,14 @@ func main() { os.Exit(1) } - core.InitializeContext(cfg.ServerDomain, cfg.PluginsDirPath, cfg.EnabledPlugins) - router := mux.NewRouter() - router.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) { - response.Write([]byte("Zirconium server is up and running!")) - }).Methods("GET") - router.HandleFunc("/ws", wsHandler) - + ac := core.NewAppContext(&cfg) logger.Info("Zirconium successfully started!") - logger.Fatal(http.ListenAndServe(":8844", router)) + logger.Fatal(ac.Run()) } -func wsHandler(w http.ResponseWriter, r *http.Request) { - ws, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Fatal(err) - } - connectionHandler.HandleNewConnection(ws) -} - -func validateConfig(config *core.ServerConfig) error { - if config.ServerDomain == "" { - return errors.New("server domain isn't specified") - } else if config.PluginsDirPath == "" { - return errors.New("plugin directory path isn't specified") +func validateConfig(config *core.Config) error { + if config.ServerID == "" { + return errors.New("server id isn't specified") } return nil } diff --git a/core/app_context.go b/core/app_context.go new file mode 100644 index 0000000..3b9bfda --- /dev/null +++ b/core/app_context.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "fmt" + "github.com/google/logger" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +type AppContext struct { + router *Router + authManager *AuthManager + connectionHandler *ConnectionHandler + websocketServer *WebsocketServer + cfg *Config + database *mongo.Database + userManager *UserManager +} + +func NewAppContext(cfg *Config) *AppContext { + var err error + appContext := &AppContext{} + appContext.cfg = cfg + + appContext.connectToDatabase() + + router, err := NewRouter(appContext) + if err != nil { + logger.Fatalf("Unable to initialize router: %s", err.Error()) + } + appContext.router = router + + authManager, err := NewAuthManager() + if err != nil { + logger.Fatalf("Unable to initialize authentication manager: %s", err.Error()) + } + appContext.authManager = authManager + + um := NewUserManager(appContext.database) + appContext.userManager = um + + connHandler := NewConnectionHandler(router) + appContext.connectionHandler = connHandler + + wss := NewWebsocketServer(cfg, connHandler) + appContext.websocketServer = wss + + return appContext +} + +func (ac *AppContext) connectToDatabase() { + ctx := context.TODO() + dbUri := fmt.Sprintf("mongodb://%s:%s@%s:%d", ac.cfg.Mongo.User, ac.cfg.Mongo.Password, ac.cfg.Mongo.Host, ac.cfg.Mongo.Port) + opts := options.Client().ApplyURI(dbUri) + err := opts.Validate() + if err != nil { + logger.Fatalf("invalid database config: %s", err) + } + mongoClient, err := mongo.Connect(ctx, opts) + if err != nil { + logger.Fatalf("cannot connect to mongo database: %s", err) + } + err = mongoClient.Ping(ctx, readpref.Primary()) + if err != nil { + logger.Fatalf("cannot connect to mongo database: %s", err) + } + + db := mongoClient.Database(ac.cfg.Mongo.Database) + ac.database = db +} + +func (ac *AppContext) Run() error { + // TODO + return nil +} diff --git a/core/auth_manager.go b/core/auth_manager.go index 93b2eeb..54e576b 100644 --- a/core/auth_manager.go +++ b/core/auth_manager.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/cadmium-im/zirconium-go/core/models" + "github.com/dgrijalva/jwt-go" ) @@ -18,8 +20,8 @@ type AuthManager struct { } type JWTCustomClaims struct { - EntityID string `json:"entityID"` - DeviceID string `json:"deviceID"` + EntityID []*models.EntityID `json:"entityID"` + DeviceID string `json:"deviceID"` jwt.StandardClaims } @@ -33,11 +35,12 @@ func NewAuthManager() (*AuthManager, error) { return am, nil } -func (am *AuthManager) CreateNewToken(entityID, deviceID string, tokenExpireTimeDuration time.Duration) (string, error) { +func (am *AuthManager) CreateNewToken(entityID *models.EntityID, deviceID string, tokenExpireTimeDuration time.Duration) (string, error) { timeNow := time.Now() expiringTime := timeNow.Add(tokenExpireTimeDuration) + claims := JWTCustomClaims{ - entityID, + []*models.EntityID{entityID}, deviceID, jwt.StandardClaims{ ExpiresAt: time.Date( @@ -61,19 +64,23 @@ func (am *AuthManager) CreateNewToken(entityID, deviceID string, tokenExpireTime return tokenString, nil } -func (am *AuthManager) ValidateToken(tokenString string) (bool, string, string, error) { +func (am *AuthManager) ValidateToken(tokenString string) (*JWTCustomClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &JWTCustomClaims{}, func(token *jwt.Token) (interface{}, error) { // Don't forget to validate the alg is what you expect: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") return []byte(am.signingKey), nil }) - if claims, ok := token.Claims.(*JWTCustomClaims); ok && token.Valid { - return true, claims.EntityID, claims.DeviceID, nil + if err != nil { + return nil, err } - return false, "", "", err + + if claims, ok := token.Claims.(*JWTCustomClaims); ok && token.Valid { + return claims, nil + } + return nil, err } diff --git a/core/builtin_plugins.go b/core/builtin_plugins.go deleted file mode 100644 index e389a48..0000000 --- a/core/builtin_plugins.go +++ /dev/null @@ -1,4 +0,0 @@ -package core - -// BuiltinPlugins is a list of builtin plugins -var BuiltinPlugins = map[string]Module{} diff --git a/core/config.go b/core/config.go index 43d11c3..d1f49c8 100644 --- a/core/config.go +++ b/core/config.go @@ -1,12 +1,18 @@ package core -type ServerConfig struct { - // A list of enabled plugins (or extensions) in server - EnabledPlugins []string `toml:"enabledPlugins" comment:"A list of enabled plugins (or extensions) in server"` - - // Server domain name (e.g. example.com) - ServerDomain string `toml:"serverDomain" comment:"Server domain name (e.g. example.com)"` - - // Path to directory with plugin executables - PluginsDirPath string `toml:"pluginsDirPath" comment:"Path to directory with plugin executables"` +type Config struct { + ServerDomains []string `comment:"Server domain names (e.g. example.com)"` + ServerID string + Websocket struct { + Host string + Port int + Endpoint string + } + Mongo struct { + Host string + Port int + User string + Password string + Database string + } } diff --git a/core/connection_handler.go b/core/connection_handler.go index c438a43..04a6bd8 100644 --- a/core/connection_handler.go +++ b/core/connection_handler.go @@ -1,26 +1,28 @@ package core import ( - "github.com/ChronosX88/zirconium/core/models" + "github.com/cadmium-im/zirconium-go/core/models" "github.com/google/logger" "github.com/google/uuid" "github.com/gorilla/websocket" ) type ConnectionHandler struct { - connections map[string]*OriginC2S + router *Router + connections map[string]*Session } -func NewConnectionHandler() *ConnectionHandler { +func NewConnectionHandler(r *Router) *ConnectionHandler { return &ConnectionHandler{ - connections: make(map[string]*OriginC2S), + router: r, + connections: make(map[string]*Session), } } func (ch *ConnectionHandler) HandleNewConnection(wsocket *websocket.Conn) { - uuid, _ := uuid.NewRandom() - uuidStr := uuid.String() - o := &OriginC2S{ + randomUUID, _ := uuid.NewRandom() + uuidStr := randomUUID.String() + o := &Session{ wsConn: wsocket, connID: uuidStr, } @@ -31,11 +33,11 @@ func (ch *ConnectionHandler) HandleNewConnection(wsocket *websocket.Conn) { err := o.wsConn.ReadJSON(&msg) if err != nil { delete(ch.connections, o.connID) - o.wsConn.Close() + _ = o.wsConn.Close() logger.Infof("Connection %s was closed. (Reason: %s)", o.connID, err.Error()) break } - router.RouteMessage(o, msg) + ch.router.RouteMessage(o, msg) } }() logger.Infof("Connection %s was created", o.connID) diff --git a/core/globals.go b/core/globals.go deleted file mode 100644 index 83fb485..0000000 --- a/core/globals.go +++ /dev/null @@ -1,41 +0,0 @@ -package core - -import ( - "github.com/google/logger" -) - -var ( - moduleMgr ModuleAPI - router *Router - authManager *AuthManager - serverDomain string - pluginManager *PluginManager -) - -func InitializeContext(sDomain string, pluginsDirPath string, enabledPlugins []string) { - var err error - moduleMgr, err = NewModuleManager() - if err != nil { - logger.Fatalf("Unable to initialize module manager: %s", err.Error()) - } - - router, err = NewRouter() - if err != nil { - logger.Fatalf("Unable to initialize router: %s", err.Error()) - } - - authManager, err = NewAuthManager() - if err != nil { - logger.Fatalf("Unable to initialize authentication manager: %s", err.Error()) - } - serverDomain = sDomain - - for _, v := range BuiltinPlugins { - go v.Initialize(ModuleAPI(moduleMgr)) // Initialize builtin plugins - } - - pluginManager = NewPluginManager() - for _, v := range enabledPlugins { - pluginManager.StartPlugin(pluginsDirPath, v, moduleMgr.(*ModuleManager)) - } -} diff --git a/core/models/auth/auth.go b/core/models/auth/auth.go new file mode 100644 index 0000000..2b4a3e3 --- /dev/null +++ b/core/models/auth/auth.go @@ -0,0 +1,11 @@ +package auth + +type AuthRequest struct { + Type string `json:"type"` + Fields map[string]interface{} `json:"fields"` +} + +type AuthResponse struct { + Token string `json:"token"` + DeviceID string `json:"deviceID"` +} diff --git a/core/models/base_message.go b/core/models/base_message.go index d19efd8..b430f02 100644 --- a/core/models/base_message.go +++ b/core/models/base_message.go @@ -5,13 +5,12 @@ type BaseMessage struct { ID string `json:"id"` MessageType string `json:"type"` From string `json:"from"` - To string `json:"to"` + To []string `json:"to"` Ok bool `json:"ok"` - AuthToken string `json:"authToken"` Payload map[string]interface{} `json:"payload"` } -func NewBaseMessage(id, messageType, from, to string, ok bool, payload map[string]interface{}) BaseMessage { +func NewBaseMessage(id, messageType, from string, to []string, ok bool, payload map[string]interface{}) BaseMessage { return BaseMessage{ ID: id, MessageType: messageType, diff --git a/core/models/entity_id.go b/core/models/entity_id.go index 9688f80..49d4288 100644 --- a/core/models/entity_id.go +++ b/core/models/entity_id.go @@ -9,8 +9,8 @@ type EntityIDType string const ( UsernameType EntityIDType = "@" - RoomAliasType EntityIDType = "#" - RoomIDType EntityIDType = "!" + ChatAliasType EntityIDType = "#" + ChatIDType EntityIDType = "!" ) type EntityID struct { @@ -19,30 +19,52 @@ type EntityID struct { ServerPart string } -func NewEntityID(entityID string) *EntityID { - eID := &EntityID{} - switch EntityIDType(string(entityID[0])) { +func NewEntityIDFromString(entityID string) (*EntityID, error) { + eid := &EntityID{} + typ := string(entityID[0]) + switch EntityIDType(typ) { case UsernameType: + fallthrough + case ChatAliasType: + fallthrough + case ChatIDType: { - eID.EntityIDType = UsernameType - } - case RoomAliasType: - { - eID.EntityIDType = RoomAliasType - } - case RoomIDType: - { - eID.EntityIDType = RoomIDType + eid.EntityIDType = EntityIDType(typ) } + default: + return nil, fmt.Errorf("invalid entity id type: %s", typ) } + localAndServerPart := strings.Split(entityID, "@") if len(localAndServerPart) == 3 { localAndServerPart = localAndServerPart[1:] } - eID.LocalPart = localAndServerPart[0] - eID.ServerPart = localAndServerPart[1] + eid.LocalPart = localAndServerPart[0] + eid.ServerPart = localAndServerPart[1] - return eID + return eid, nil +} + +func NewEntityID(typ, localPart, serverPart string) (*EntityID, error) { + eid := &EntityID{} + + switch EntityIDType(typ) { + case UsernameType: + fallthrough + case ChatAliasType: + fallthrough + case ChatIDType: + { + eid.EntityIDType = EntityIDType(typ) + } + default: + return nil, fmt.Errorf("invalid entity id type: %s", typ) + } + + eid.LocalPart = localPart + eid.ServerPart = serverPart + + return eid, nil } func (eID *EntityID) String() string { diff --git a/core/models/protocol_error.go b/core/models/protocol_error.go index 604fd28..9f13a4d 100644 --- a/core/models/protocol_error.go +++ b/core/models/protocol_error.go @@ -1,7 +1,7 @@ package models type ProtocolError struct { - ErrCode string `json:"errCode"` - ErrText string `json:"errText"` - ErrPayload map[string]interface{} `json:"errPayload"` + ErrCode string `json:"code"` + ErrText string `json:"text"` + ErrPayload map[string]interface{} `json:"payload"` } diff --git a/core/module.go b/core/module.go deleted file mode 100644 index a25135c..0000000 --- a/core/module.go +++ /dev/null @@ -1,17 +0,0 @@ -package core - -const ( - ModuleInterfaceName = "Module" -) - -type Module interface { - Initialize(moduleAPI ModuleAPI) - Name() string - Version() string -} - -type ModuleRef struct { - F func() Module -} - -type ModuleFunc func() Module diff --git a/core/module_manager.go b/core/module_manager.go deleted file mode 100644 index 19f7749..0000000 --- a/core/module_manager.go +++ /dev/null @@ -1,100 +0,0 @@ -package core - -import ( - "reflect" - "sync" - "time" - - "github.com/ChronosX88/zirconium/core/models" -) - -type C2SMessageHandler struct { - HandlerFunc func(origin *OriginC2S, message models.BaseMessage) - AnonymousAllowed bool -} - -type ModuleManager struct { - moduleMutex sync.Mutex - c2sMessageHandlers map[string][]*C2SMessageHandler - coreEventHandlers map[string][]func(sourceModuleName string, event map[string]interface{}) -} - -type ModuleAPI interface { - Hook(messageType string, anonymousAllowed bool, handlerFunc func(origin *OriginC2S, message models.BaseMessage)) - HookInternalEvent(eventName string, handlerFunc func(sourceModuleName string, event map[string]interface{})) - Unhook(messageType string, handlerFunc func(origin *OriginC2S, message models.BaseMessage)) - UnhookInternalEvent(eventName string, handlerFunc func(sourceModuleName string, event map[string]interface{})) - FireEvent(sourceModuleName string, eventName string, eventPayload map[string]interface{}) - GenerateToken(entityID, deviceID string, tokenExpireTimeDuration time.Duration) (string, error) - GetServerDomain() string -} - -func NewModuleManager() (ModuleAPI, error) { - var mm = &ModuleManager{ - c2sMessageHandlers: make(map[string][]*C2SMessageHandler), - coreEventHandlers: make(map[string][]func(sourceModuleName string, event map[string]interface{})), - } - return mm, nil -} - -func (mm *ModuleManager) Hook(messageType string, anonymousAllowed bool, handlerFunc func(origin *OriginC2S, message models.BaseMessage)) { - mm.moduleMutex.Lock() - mm.c2sMessageHandlers[messageType] = append(mm.c2sMessageHandlers[messageType], &C2SMessageHandler{ - HandlerFunc: handlerFunc, - AnonymousAllowed: anonymousAllowed, - }) - mm.moduleMutex.Unlock() -} - -func (mm *ModuleManager) HookInternalEvent(eventName string, handlerFunc func(sourceModuleName string, event map[string]interface{})) { - mm.moduleMutex.Lock() - mm.coreEventHandlers[eventName] = append(mm.coreEventHandlers[eventName], handlerFunc) - mm.moduleMutex.Unlock() -} - -func (mm *ModuleManager) Unhook(messageType string, handlerFunc func(origin *OriginC2S, message models.BaseMessage)) { - mm.moduleMutex.Lock() - defer mm.moduleMutex.Unlock() - var handlers = mm.c2sMessageHandlers[messageType] - if handlers != nil { - for i, v := range handlers { - if reflect.ValueOf(v.HandlerFunc).Pointer() == reflect.ValueOf(handlerFunc).Pointer() { - handlers[i] = handlers[len(handlers)-1] - handlers[len(handlers)-1] = nil - handlers = handlers[:len(handlers)-1] - mm.c2sMessageHandlers[messageType] = handlers - break - } - } - } -} - -func (mm *ModuleManager) UnhookInternalEvent(eventName string, handlerFunc func(sourceModuleName string, event map[string]interface{})) { - mm.moduleMutex.Lock() - defer mm.moduleMutex.Unlock() - var handlers = mm.coreEventHandlers[eventName] - if handlers != nil { - for i, v := range handlers { - if reflect.ValueOf(v).Pointer() == reflect.ValueOf(handlerFunc).Pointer() { - handlers[i] = handlers[len(handlers)-1] - handlers[len(handlers)-1] = nil - handlers = handlers[:len(handlers)-1] - mm.coreEventHandlers[eventName] = handlers - break - } - } - } -} - -func (mm *ModuleManager) FireEvent(sourceModuleName string, eventName string, eventPayload map[string]interface{}) { - router.RoutecoreEvent(sourceModuleName, eventName, eventPayload) -} - -func (mm *ModuleManager) GenerateToken(entityID, deviceID string, tokenExpireTimeDuration time.Duration) (string, error) { - token, err := authManager.CreateNewToken(entityID, deviceID, tokenExpireTimeDuration) - return token, err -} - -func (mm *ModuleManager) GetServerDomain() string { - return serverDomain -} diff --git a/core/module_rpc_receiver.go b/core/module_rpc_receiver.go deleted file mode 100644 index 45c183d..0000000 --- a/core/module_rpc_receiver.go +++ /dev/null @@ -1,38 +0,0 @@ -package core - -import ( - "errors" - - "github.com/hashicorp/go-plugin" -) - -type greeterServer struct { - Broker *plugin.MuxBroker - Module Module -} - -// Server implmentation of go-plugin.plugin.Plugin.Server -func (p *ModuleRef) Server(b *plugin.MuxBroker) (interface{}, error) { - if p.F == nil { - return nil, errors.New("Greeter interface not implemeted") - } - return &greeterServer{Broker: b, Module: p.F()}, nil -} - -// Name calls the plugin implementation to get the name of the plugin -func (p *greeterServer) Name(nothing interface{}, result *string) error { - *result = p.Module.Name() - return nil -} - -// Version calls the plugin implementation to get the version of the plugin -func (p *greeterServer) Version(nothing interface{}, result *string) error { - *result = p.Module.Version() - return nil -} - -// StartTime calls the plugin implementation to initialize plugin -func (p *greeterServer) Initialize(moduleAPI ModuleAPI, emptyResult interface{}) error { - p.Module.Initialize(moduleAPI) - return nil -} diff --git a/core/module_rpc_sender.go b/core/module_rpc_sender.go deleted file mode 100644 index 151e975..0000000 --- a/core/module_rpc_sender.go +++ /dev/null @@ -1,49 +0,0 @@ -package core - -import ( - "net/rpc" - - "github.com/google/logger" - "github.com/hashicorp/go-plugin" -) - -type moduleClient struct { - Broker *plugin.MuxBroker - Client *rpc.Client -} - -// Client implmentation of go-plugin.plugin.Plugin.Client -func (p *ModuleRef) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &moduleClient{Broker: b, Client: c}, nil -} - -// Name initiates an RPC call to the plugin name -func (p *moduleClient) Name() string { - var resp string - err := p.Client.Call("Plugin.Name", new(interface{}), &resp) - if err != nil { - logger.Fatal(err) // FIXME - } - return resp -} - -// Version initiates an RPC call to the plugin version -func (p *moduleClient) Version() string { - var resp string - err := p.Client.Call("Plugin.Version", new(interface{}), &resp) - if err != nil { - logger.Fatal(err) // FIXME - } - return resp -} - -// StartTime initiates an RPC call to the plugin for initializing -func (p *moduleClient) Initialize(moduleAPI ModuleAPI) { - var resp interface{} - err := p.Client.Call("Plugin.Initialize", map[string]interface{}{ - "moduleAPI": moduleAPI, - }, &resp) - if err != nil { - logger.Fatal(err) // FIXME - } -} diff --git a/core/origin_c2s.go b/core/origin_c2s.go deleted file mode 100644 index 91efebc..0000000 --- a/core/origin_c2s.go +++ /dev/null @@ -1,17 +0,0 @@ -package core - -import ( - "github.com/ChronosX88/zirconium/core/models" - "github.com/gorilla/websocket" -) - -type OriginC2S struct { - wsConn *websocket.Conn - connID string - entityID *models.EntityID - deviceID *string -} - -func (o *OriginC2S) Send(message models.BaseMessage) error { - return o.wsConn.WriteJSON(message) -} diff --git a/core/plugin_manager.go b/core/plugin_manager.go deleted file mode 100644 index e67a604..0000000 --- a/core/plugin_manager.go +++ /dev/null @@ -1,56 +0,0 @@ -package core - -import ( - "os" - "os/exec" - "path/filepath" - - "github.com/google/logger" - "github.com/hashicorp/go-plugin" -) - -type PluginManager struct{} - -func NewPluginManager() *PluginManager { - return &PluginManager{} -} - -func (p *PluginManager) StartPlugin(pluginsDirPath, pluginFile string, moduleManager ModuleAPI) error { - pluginsDirectory, _ := filepath.Abs(filepath.Dir(pluginsDirPath)) - pluginFile = filepath.Join(pluginsDirectory, pluginFile) - - logger.Info("Starting plugin: %s", pluginFile) - - client := plugin.NewClient(&plugin.ClientConfig{ - Cmd: exec.Command(pluginFile), - Managed: true, - SyncStdout: os.Stdout, - SyncStderr: os.Stderr, - - HandshakeConfig: HandshakeConfig, - Plugins: GetPluginMap(nil), - }) - - rpcclient, err := client.Client() - - if err != nil { - logger.Errorf("Failed to get RPC Client: %s", err) - client.Kill() - return err - } - - // get the interface - raw, err := rpcclient.Dispense(ModuleInterfaceName) - if err != nil { - logger.Errorf("Failed to get interface: %s error: %s", ModuleInterfaceName, err) - return err - } - - moduleObj := raw.(Module) - - go func() { - moduleObj.Initialize(moduleManager) - }() - - return nil -} diff --git a/core/router.go b/core/router.go index ecfa8dd..021603b 100644 --- a/core/router.go +++ b/core/router.go @@ -1,56 +1,43 @@ package core import ( - "github.com/ChronosX88/zirconium/core/models" + "github.com/cadmium-im/zirconium-go/core/models" "github.com/google/logger" ) type Router struct { - moduleManager *ModuleManager - connections []*OriginC2S + appContext *AppContext + handlers map[string][]C2SMessageHandler + connections []*Session } -func NewRouter() (*Router, error) { - mm, err := NewModuleManager() - if err != nil { - return nil, err - } +type C2SMessageHandler interface { + HandleMessage(s *Session, message models.BaseMessage) + IsAuthorizationRequired() bool + HandlingType() string +} + +func NewRouter(ctx *AppContext) (*Router, error) { r := &Router{ - moduleManager: mm.(*ModuleManager), + appContext: ctx, + handlers: map[string][]C2SMessageHandler{}, } return r, nil } -func (r *Router) RouteMessage(origin *OriginC2S, message models.BaseMessage) { - handlers := r.moduleManager.c2sMessageHandlers[message.MessageType] +func (r *Router) RouteMessage(origin *Session, message models.BaseMessage) { + handlers := r.handlers[message.MessageType] if handlers != nil { for _, v := range handlers { - if !v.AnonymousAllowed { - var entityID, deviceID string - var isValid bool - var err error - if message.AuthToken != "" { - isValid, entityID, deviceID, err = authManager.ValidateToken(message.AuthToken) - if err != nil || !isValid { - logger.Warningf("Connection %s isn't authorized", origin.connID) - msg := PrepareMessageUnauthorized(message) - origin.Send(msg) - } - } else { + if v.IsAuthorizationRequired() { + if len(origin.entityID) == 0 { logger.Warningf("Connection %s isn't authorized", origin.connID) - msg := PrepareMessageUnauthorized(message) - origin.Send(msg) - } - - if origin.entityID == nil { - origin.entityID = models.NewEntityID(entityID) - } - if origin.deviceID == nil { - origin.deviceID = &deviceID + msg := PrepareMessageUnauthorized(message, r.appContext.cfg.ServerDomains[0]) // fixme: domain + _ = origin.Send(msg) } } - go v.HandlerFunc(origin, message) + go v.HandleMessage(origin, message) } } else { protocolError := models.ProtocolError{ @@ -58,19 +45,12 @@ func (r *Router) RouteMessage(origin *OriginC2S, message models.BaseMessage) { ErrText: "Server doesn't implement message type " + message.MessageType, ErrPayload: make(map[string]interface{}), } - errMsg := models.NewBaseMessage(message.ID, message.MessageType, serverDomain, message.From, false, StructToMap(protocolError)) + errMsg := models.NewBaseMessage(message.ID, message.MessageType, r.appContext.cfg.ServerID, []string{message.From}, false, StructToMap(protocolError)) logger.Infof("Drop message with type %s because server hasn't proper handlers", message.MessageType) - origin.Send(errMsg) + _ = origin.Send(errMsg) } } -func (r *Router) RoutecoreEvent(sourceModuleName string, eventName string, eventPayload map[string]interface{}) { - handlers := r.moduleManager.coreEventHandlers[eventName] - if handlers != nil { - for _, v := range handlers { - go v(sourceModuleName, eventPayload) - } - } else { - logger.Infof("Drop event %s because server hasn't proper handlers", eventName) - } +func (r *Router) RegisterC2SHandler(c C2SMessageHandler) { + r.handlers[c.HandlingType()] = append(r.handlers[c.HandlingType()], c) } diff --git a/core/session.go b/core/session.go new file mode 100644 index 0000000..1062955 --- /dev/null +++ b/core/session.go @@ -0,0 +1,21 @@ +package core + +import ( + "github.com/cadmium-im/zirconium-go/core/models" + "github.com/gorilla/websocket" +) + +type Session struct { + wsConn *websocket.Conn + connID string + entityID []*models.EntityID + deviceID *string +} + +func (s *Session) Send(message models.BaseMessage) error { + return s.wsConn.WriteJSON(message) +} + +func (s *Session) Close() error { + return s.wsConn.Close() +} diff --git a/core/user_manager.go b/core/user_manager.go new file mode 100644 index 0000000..6e38154 --- /dev/null +++ b/core/user_manager.go @@ -0,0 +1,21 @@ +package core + +import "go.mongodb.org/mongo-driver/mongo" + +const ( + UsersCollectionName = "users" +) + +type UserManager struct { + usersCol *mongo.Collection +} + +func NewUserManager(db *mongo.Database) *UserManager { + col := db.Collection(UsersCollectionName) + + um := &UserManager{ + usersCol: col, + } + + return um +} diff --git a/core/utils.go b/core/utils.go index 55869ef..57c27af 100644 --- a/core/utils.go +++ b/core/utils.go @@ -2,12 +2,9 @@ package core import ( "crypto/rand" - "log" "reflect" - "github.com/ChronosX88/zirconium/core/models" - "github.com/google/logger" - "github.com/hashicorp/go-plugin" + "github.com/cadmium-im/zirconium-go/core/models" ) func GenRandomBytes(size int) (blk []byte, err error) { @@ -42,58 +39,12 @@ func StructToMap(item interface{}) map[string]interface{} { return res } -func PrepareMessageUnauthorized(msg models.BaseMessage) models.BaseMessage { +func PrepareMessageUnauthorized(msg models.BaseMessage, serverDomain string) models.BaseMessage { protocolError := models.ProtocolError{ ErrCode: "unauthorized", ErrText: "Unauthorized access", ErrPayload: make(map[string]interface{}), } - errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverDomain, msg.From, false, StructToMap(protocolError)) + errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverDomain, []string{msg.From}, false, StructToMap(protocolError)) return errMsg } - -var HandshakeConfig = plugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "TEST_TEST", - MagicCookieValue: "qwerty", -} - -type PluginOpts struct { - Module ModuleFunc - RunAsPlugin bool -} - -// GetPluginMap returns the plugin map defined Hashicorp go-plugin. -// The reserved parameter should only be used by the RPC receiver (the plugin). -// Otherwise, reserved should be nil for the RPC sender (the mainapp). -func GetPluginMap(reserved *PluginOpts) map[string]plugin.Plugin { - var moduleObj ModuleRef - - if reserved != nil { - moduleObj.F = reserved.Module - } - - return map[string]plugin.Plugin{ - ModuleInterfaceName: &moduleObj, - } -} - -func StartPlugin(opts *PluginOpts, quit chan bool) { - if opts.RunAsPlugin { - go func() { - logger.Info("Starting plugin communication...") - - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: HandshakeConfig, - Plugins: GetPluginMap(opts), - }) - - logger.Info("Exiting plugin communication...") - - quit <- true - logger.Info("Exiting plugin...") - }() - } else { - log.Println("Starting in standalone mode...") - } -} diff --git a/core/websocket_server.go b/core/websocket_server.go new file mode 100644 index 0000000..6136e40 --- /dev/null +++ b/core/websocket_server.go @@ -0,0 +1,49 @@ +package core + +import ( + "fmt" + "github.com/google/logger" + "github.com/gorilla/mux" + "github.com/gorilla/websocket" + "net/http" +) + +var wsUpgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +type WebsocketServer struct { + r *mux.Router + connHandler *ConnectionHandler + cfg *Config +} + +func NewWebsocketServer(cfg *Config, connHandler *ConnectionHandler) *WebsocketServer { + wss := &WebsocketServer{} + + wss.connHandler = connHandler + wss.cfg = cfg + r := mux.NewRouter() + wss.r = r + r.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) { + _, _ = response.Write([]byte("Zirconium server is up and running!")) + }).Methods("GET") + r.HandleFunc(cfg.Websocket.Endpoint, func(w http.ResponseWriter, r *http.Request) { + ws, err := wsUpgrader.Upgrade(w, r, nil) + if err != nil { + logger.Errorf(err.Error()) + return + } + wss.connHandler.HandleNewConnection(ws) + }) + + return wss +} + +func (wss *WebsocketServer) Run() error { + addr := fmt.Sprintf("%s:%d", wss.cfg.Websocket.Host, wss.cfg.Websocket.Port) + + return http.ListenAndServe(addr, wss.r) +} diff --git a/go.mod b/go.mod index 70eb729..a671728 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,14 @@ -module github.com/ChronosX88/zirconium +module github.com/cadmium-im/zirconium-go go 1.13 require ( + github.com/BurntSushi/toml v0.3.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/google/logger v1.0.1 github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.1 - github.com/hashicorp/go-plugin v1.0.1 - github.com/pelletier/go-toml v1.6.0 + github.com/pelletier/go-toml v1.7.0 + go.mongodb.org/mongo-driver v1.5.1 ) diff --git a/go.sum b/go.sum index 9723e93..ed32168 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,43 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/logger v1.0.1 h1:Jtq7/44yDwUXMaLTYgXFC31zpm6Oku7OI/k4//yVANQ= github.com/google/logger v1.0.1/go.mod h1:w7O8nrRr0xufejBlQMI83MXqRusvREoJdaAxV+CoAB4= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -17,37 +46,97 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= -github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI= +go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/.gitkeep b/plugins/.gitkeep deleted file mode 100644 index e69de29..0000000