Skip to content

Commit

Permalink
add acl to memes
Browse files Browse the repository at this point in the history
  • Loading branch information
fsufitch committed Feb 6, 2020
1 parent 01bcdd7 commit c28c871
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 2 deletions.
27 changes: 27 additions & 0 deletions bot/memelink-module/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package memelink

import "github.com/bwmarrin/discordgo"

const memeEditorACL = "boarbot.modules.memelink::edit"

func (m Module) isMemeEditor(s *discordgo.Session, userID string, guildID string) bool {
if allowed, err := m.ACLDAO.CheckUserACL(userID, memeEditorACL); err != nil {
m.Log.Errorf("could not check meme editor user ACL: %v", err)
return false
} else if allowed {
return true
}

member, err := s.GuildMember(guildID, userID)
if err != nil {
m.Log.Errorf("could not obtain member from user: %v", err)
return false
}

allowed, err := m.ACLDAO.CheckMultiRoleACL(member.Roles, memeEditorACL)
if err != nil {
m.Log.Errorf("could not check meme editor roles ACL: %v", err)
return false
}
return allowed
}
10 changes: 10 additions & 0 deletions bot/memelink-module/cmd_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ func (m Module) handleCommand(s *discordgo.Session, event *discordgo.MessageCrea

func (m Module) handleAddMeme(s *discordgo.Session, event *discordgo.MessageCreate,
name string, url string, appendOK bool) error {
if !m.isMemeEditor(s, event.Author.ID, event.GuildID) {
_, err := s.ChannelMessageSend(event.Message.ChannelID, "Sorry, you do not have meme editor permissions")
return err
}

name = strings.TrimSpace(name)
url = strings.TrimSpace(url)

Expand Down Expand Up @@ -150,6 +155,11 @@ func (m Module) handleAddMeme(s *discordgo.Session, event *discordgo.MessageCrea

func (m Module) handleAddAlias(s *discordgo.Session, event *discordgo.MessageCreate,
newName string, oldName string) error {
if !m.isMemeEditor(s, event.Author.ID, event.GuildID) {
_, err := s.ChannelMessageSend(event.Message.ChannelID, "Sorry, you do not have meme editor permissions")
return err
}

if newName == "" || oldName == "" {
_, err := s.ChannelMessageSend(event.Message.ChannelID, "aliasing requires two arguments for meme names (new name, old name)")
return err
Expand Down
2 changes: 2 additions & 0 deletions bot/memelink-module/memelink.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/bwmarrin/discordgo"
"github.com/fsufitch/discord-boar-bot/db/acl-dao"
"github.com/fsufitch/discord-boar-bot/db/memes-dao"
"github.com/fsufitch/discord-boar-bot/log"
)
Expand All @@ -12,6 +13,7 @@ import (
type Module struct {
Log *log.Logger
MemeDAO *memes.DAO
ACLDAO *acl.DAO

session *discordgo.Session
}
Expand Down
4 changes: 2 additions & 2 deletions bot/memelink-module/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package memelink

import "github.com/google/wire"

// ProvideMemeLinkModule provides everything needed to build a meme link module
// ProvideModule provides everything needed to build a meme link module
var ProvideModule = wire.NewSet(
wire.Struct(new(Module), "Log", "MemeDAO"),
wire.Struct(new(Module), "Log", "MemeDAO", "ACLDAO"),
)
5 changes: 5 additions & 0 deletions cmd/discord-boar-bot/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions db/acl-dao/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package acl

import (
"github.com/fsufitch/discord-boar-bot/db/connection"
)

// DAO exposes database ACL functionality
type DAO struct {
Conn connection.DatabaseConnection
}
100 changes: 100 additions & 0 deletions db/acl-dao/role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package acl

import (
"database/sql"
"fmt"
"strings"
)

// RoleACL encapsulates a single permission granted to a role
type RoleACL struct {
RowID int
ACLID string
UserID string
Details string
}

// CheckRoleACL checks whether the role is granted the permission
func (dao DAO) CheckRoleACL(roleID string, aclID string) (bool, error) {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return false, err
}
defer tx.Rollback()

row := tx.QueryRow(`
SELECT COUNT(1)
FROM role_acl
WHERE role_id=$1 AND acl_id=$2
`, roleID, aclID)

var result int
err = row.Scan(&result)
return result > 0, err
}

// CheckMultiRoleACL checks whether any of the roles are granted the permission
func (dao DAO) CheckMultiRoleACL(roleIDs []string, aclID string) (bool, error) {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return false, err
}
defer tx.Rollback()

queryParams := []interface{}{aclID}
inValueList := []string{}
for i, roleID := range roleIDs {
queryParams = append(queryParams, roleID)
inValueList = append(inValueList, fmt.Sprintf("$%d", i+2))
}
inValue := "(" + strings.Join(inValueList, ", ") + ")"
query := fmt.Sprintf(`
SELECT COUNT(1)
FROM role_acl
WHERE acl_id=$1 AND role_id IN %s
`, inValue)

row := tx.QueryRow(query, queryParams...)

var result int
err = row.Scan(&result)
return result > 0, err
}

// GrantRoleACL grants a permission to a role
func (dao DAO) GrantRoleACL(roleID string, aclID string, details string) error {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return err
}
defer tx.Rollback()

_, err = tx.Exec(`
INSERT INTO role_acl (role_id, acl_id, details)
VALUES ($1, $2, $3)
`, roleID, aclID, details)
if err != nil {
return err
}

return tx.Commit()
}

// RevokeRoleACL revokes a permission from a role
func (dao DAO) RevokeRoleACL(roleID string, aclID string) error {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return err
}
defer tx.Rollback()

_, err = tx.Exec(`
DELETE FROM role_acl
WHERE role_id=$1 AND acl_id=$2
`, roleID, aclID)
if err != nil {
return err
}

return tx.Commit()
}
68 changes: 68 additions & 0 deletions db/acl-dao/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package acl

import "database/sql"

// UserACL encapsulates a single permission granted to a user
type UserACL struct {
RowID int
ACLID string
UserID string
Details string
}

// CheckUserACL checks whether the user is granted the permission
func (dao DAO) CheckUserACL(userID string, aclID string) (bool, error) {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return false, err
}
defer tx.Rollback()

row := tx.QueryRow(`
SELECT COUNT(1)
FROM user_acl
WHERE user_id=$1 AND acl_id=$2
`, userID, aclID)

var result int
err = row.Scan(&result)
return result > 0, err
}

// GrantUserACL grants a permission to a user
func (dao DAO) GrantUserACL(userID string, aclID string, details string) error {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return err
}
defer tx.Rollback()

_, err = tx.Exec(`
INSERT INTO user_acl (user_id, acl_id, details)
VALUES ($1, $2, $3)
`, userID, aclID, details)
if err != nil {
return err
}

return tx.Commit()
}

// RevokeUserACL revokes a permission from a user
func (dao DAO) RevokeUserACL(userID string, aclID string) error {
tx, err := (*sql.DB)(dao.Conn).Begin()
if err != nil {
return err
}
defer tx.Rollback()

_, err = tx.Exec(`
DELETE FROM user_acl
WHERE user_id=$1 AND acl_id=$2
`, userID, aclID)
if err != nil {
return err
}

return tx.Commit()
}
2 changes: 2 additions & 0 deletions db/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package db

import (
"github.com/fsufitch/discord-boar-bot/db/acl-dao"
"github.com/fsufitch/discord-boar-bot/db/connection"
"github.com/fsufitch/discord-boar-bot/db/kv-dao"
"github.com/fsufitch/discord-boar-bot/db/memes-dao"
Expand All @@ -12,4 +13,5 @@ var ProvidePostgresDatabase = wire.NewSet(
connection.ProvidePostgresDatabaseConnection,
wire.Struct(new(kv.DAO), "*"),
wire.Struct(new(memes.DAO), "*"),
wire.Struct(new(acl.DAO), "*"),
)

0 comments on commit c28c871

Please sign in to comment.