mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-10 10:20:08 +08:00
360 lines
10 KiB
Go
360 lines
10 KiB
Go
package acl
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
type Plugin struct {
|
|
name string
|
|
commands []utils.Command
|
|
categories []string
|
|
description string
|
|
acl *ACL
|
|
}
|
|
|
|
var ACLPlugin Plugin
|
|
|
|
func (p Plugin) Name() string {
|
|
return p.name
|
|
}
|
|
|
|
func (p Plugin) Commands() []utils.Command {
|
|
return p.commands
|
|
}
|
|
|
|
func (p Plugin) Description() string {
|
|
return p.description
|
|
}
|
|
|
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
|
if strings.EqualFold(cmd[0], "auth") {
|
|
return p.handleAuth(ctx, cmd, server, conn)
|
|
}
|
|
if strings.EqualFold(cmd[0], "acl") {
|
|
switch strings.ToLower(cmd[1]) {
|
|
default:
|
|
return nil, errors.New("not implemented")
|
|
case "getuser":
|
|
return p.handleGetUser(ctx, cmd, server, conn)
|
|
case "cat":
|
|
return p.handleCat(ctx, cmd, server)
|
|
case "users":
|
|
return p.handleUsers(ctx, cmd, server)
|
|
case "setuser":
|
|
return p.handleSetUser(ctx, cmd, server)
|
|
case "deluser":
|
|
return p.handleDelUser(ctx, cmd, server)
|
|
case "whoami":
|
|
return p.handleWhoAmI(ctx, cmd, server, conn)
|
|
case "list":
|
|
return p.handleList(ctx, cmd, server)
|
|
case "load":
|
|
return p.handleLoad(ctx, cmd, server)
|
|
case "save":
|
|
return p.handleSave(ctx, cmd, server)
|
|
}
|
|
}
|
|
return nil, errors.New("not implemented")
|
|
}
|
|
|
|
func (p Plugin) handleAuth(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
|
if len(cmd) < 2 || len(cmd) > 3 {
|
|
return nil, errors.New(utils.WRONG_ARGS_RESPONSE)
|
|
}
|
|
if err := p.acl.AuthenticateConnection(conn, cmd); err != nil {
|
|
return nil, err
|
|
}
|
|
return []byte(utils.OK_RESPONSE), nil
|
|
}
|
|
|
|
func (p Plugin) handleGetUser(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
|
return nil, errors.New("ACL GET USER not implemented")
|
|
}
|
|
|
|
func (p Plugin) handleCat(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
if len(cmd) > 3 {
|
|
return nil, errors.New(utils.WRONG_ARGS_RESPONSE)
|
|
}
|
|
|
|
categories := make(map[string][]string)
|
|
|
|
commands := server.GetAllCommands(ctx)
|
|
|
|
for _, command := range commands {
|
|
if len(command.SubCommands) == 0 {
|
|
for _, category := range command.Categories {
|
|
categories[category] = append(categories[category], command.Command)
|
|
}
|
|
continue
|
|
}
|
|
for _, subcommand := range command.SubCommands {
|
|
for _, category := range subcommand.Categories {
|
|
categories[category] = append(categories[category],
|
|
fmt.Sprintf("%s|%s", command.Command, subcommand.Command))
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(cmd) == 2 {
|
|
var cats []string
|
|
length := 0
|
|
for key, _ := range categories {
|
|
cats = append(cats, key)
|
|
length += 1
|
|
}
|
|
res := fmt.Sprintf("*%d", length)
|
|
for i, cat := range cats {
|
|
res = fmt.Sprintf("%s\r\n+%s", res, cat)
|
|
if i == len(cats)-1 {
|
|
res = res + "\r\n\n"
|
|
}
|
|
}
|
|
return []byte(res), nil
|
|
}
|
|
|
|
if len(cmd) == 3 {
|
|
var res string
|
|
for category, commands := range categories {
|
|
if strings.EqualFold(category, cmd[2]) {
|
|
res = fmt.Sprintf("*%d", len(commands))
|
|
for i, command := range commands {
|
|
res = fmt.Sprintf("%s\r\n+%s", res, command)
|
|
if i == len(commands)-1 {
|
|
res = res + "\r\n\n"
|
|
}
|
|
}
|
|
return []byte(res), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("category not found")
|
|
}
|
|
|
|
func (p Plugin) handleUsers(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
res := fmt.Sprintf("*%d", len(p.acl.Users))
|
|
for _, user := range p.acl.Users {
|
|
res += fmt.Sprintf("\r\n$%d\r\n%s", len(user.Username), user.Username)
|
|
}
|
|
res += "\r\n\n"
|
|
return []byte(res), nil
|
|
}
|
|
|
|
func (p Plugin) handleSetUser(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
return nil, errors.New("ACL SETUSER not implemented")
|
|
}
|
|
|
|
func (p Plugin) handleDelUser(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
return nil, errors.New("ACL DELUSER not implemented")
|
|
}
|
|
|
|
func (p Plugin) handleWhoAmI(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
|
connectionInfo := p.acl.Connections[conn]
|
|
return []byte(fmt.Sprintf("+%s\r\n\n", connectionInfo.User.Username)), nil
|
|
}
|
|
|
|
func (p Plugin) handleList(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
if len(cmd) > 2 {
|
|
return nil, errors.New(utils.WRONG_ARGS_RESPONSE)
|
|
}
|
|
res := fmt.Sprintf("*%d", len(p.acl.Users))
|
|
s := ""
|
|
for _, user := range p.acl.Users {
|
|
s = user.Username
|
|
// User enabled
|
|
if user.Enabled {
|
|
s += " on"
|
|
} else {
|
|
s += " off"
|
|
}
|
|
// Passwords
|
|
for _, password := range user.Passwords {
|
|
if strings.EqualFold(password.PasswordType, "plaintext") {
|
|
s += fmt.Sprintf(" %s", password.PasswordValue)
|
|
}
|
|
if strings.EqualFold(password.PasswordType, "SHA256") {
|
|
s += fmt.Sprintf(" #%s", password.PasswordValue)
|
|
}
|
|
}
|
|
// Included categories
|
|
for _, category := range user.IncludedCategories {
|
|
if category == "*" {
|
|
s += " +@all"
|
|
continue
|
|
}
|
|
s += fmt.Sprintf(" +@%s", category)
|
|
}
|
|
// Excluded categories
|
|
for _, category := range user.ExcludedCategories {
|
|
if category == "*" {
|
|
s += " -@all"
|
|
continue
|
|
}
|
|
s += fmt.Sprintf(" -@%s", category)
|
|
}
|
|
// Included commands
|
|
for _, command := range user.IncludedCommands {
|
|
if command == "*" {
|
|
s += " +all"
|
|
continue
|
|
}
|
|
s += fmt.Sprintf(" +%s", command)
|
|
}
|
|
// Excluded commands
|
|
for _, command := range user.ExcludedCommands {
|
|
if command == "*" {
|
|
s += " -all"
|
|
continue
|
|
}
|
|
s += fmt.Sprintf(" -%s", command)
|
|
}
|
|
// Included keys
|
|
for _, key := range user.IncludedKeys {
|
|
s += fmt.Sprintf(" %s~%s", "%RW", key)
|
|
}
|
|
// Included read keys
|
|
for _, key := range user.IncludedReadKeys {
|
|
s += fmt.Sprintf(" %s~%s", "%R", key)
|
|
}
|
|
// Included write keys
|
|
for _, key := range user.IncludedReadKeys {
|
|
s += fmt.Sprintf(" %s~%s", "%W", key)
|
|
}
|
|
// Included Pub/Sub channels
|
|
for _, channel := range user.IncludedPubSubChannels {
|
|
s += fmt.Sprintf(" +&%s", channel)
|
|
}
|
|
// Excluded Pup/Sub channels
|
|
for _, channel := range user.ExcludedPubSubChannels {
|
|
s += fmt.Sprintf(" -&%s", channel)
|
|
}
|
|
res = res + fmt.Sprintf("\r\n$%d\r\n%s", len(s), s)
|
|
}
|
|
|
|
res = res + "\r\n\n"
|
|
return []byte(res), nil
|
|
}
|
|
|
|
func (p Plugin) handleLoad(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
return nil, errors.New("ACL LOAD not implemented")
|
|
}
|
|
|
|
func (p Plugin) handleSave(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
|
fmt.Println(p.acl)
|
|
return nil, errors.New("ACL SAVE not implemented")
|
|
}
|
|
|
|
func NewModule(acl *ACL) Plugin {
|
|
ACLPlugin = Plugin{
|
|
acl: acl,
|
|
name: "ACLCommands",
|
|
commands: []utils.Command{
|
|
{
|
|
Command: "auth",
|
|
Categories: []string{utils.ConnectionCategory, utils.SlowCategory},
|
|
Description: "(AUTH [username] password) Authenticates the connection",
|
|
Sync: false,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "acl",
|
|
Categories: []string{},
|
|
Description: "Access-Control-List commands",
|
|
Sync: false,
|
|
SubCommands: []utils.SubCommand{
|
|
{
|
|
Command: "cat",
|
|
Categories: []string{utils.SlowCategory},
|
|
Description: "(ACL CAT [category]) List all the categories and commands inside a category.",
|
|
Sync: false,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "users",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL USERS) List all usersnames of the configured ACL users",
|
|
Sync: false,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "setuser",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL SETUSER) Configure a new or existing user",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "getuser",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL GETUSER) List the ACL rules of a user",
|
|
Sync: false,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "deluser",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL DELUSER) Deletes users and terminates their connections",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "whoami",
|
|
Categories: []string{utils.FastCategory},
|
|
Description: "(ACL WHOAMI) Returns the authenticated user of the current connection",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "list",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL LIST) Dumps effective acl rules in acl config file format",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "load",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL LOAD) Reloads the rules from the configured ACL config file",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
{
|
|
Command: "save",
|
|
Categories: []string{utils.AdminCategory, utils.SlowCategory, utils.DangerousCategory},
|
|
Description: "(ACL SAVE) Saves the effective ACL rules the configured ACL config file",
|
|
Sync: true,
|
|
KeyExtractionFunc: func(cmd []string) ([]string, error) {
|
|
return []string{}, nil
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
description: "Internal plugin to handle ACL commands",
|
|
}
|
|
return ACLPlugin
|
|
}
|