Skip to content

Commit

Permalink
Merge pull request #4 from nghenhan/feat/get-approver-from-database
Browse files Browse the repository at this point in the history
feat: get approver from database
  • Loading branch information
datphamcode295 authored Dec 23, 2024
2 parents 3ffa074 + adf3be3 commit b7aad9a
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ WATCHER_LIST=tctl requests list

# Path to Teleport authentication credentials
AUTH_PEM=base64-encoded-auth-pem

# Database URL for Teleport Audit Log
DB_URL=postgresql://postgres:password@localhost:5432/teleport_discord?sslmode=disable
3 changes: 3 additions & 0 deletions cmd/teleport-discord-bot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/dwarvesf/teleport-discord-bot/internal/config"
"github.com/dwarvesf/teleport-discord-bot/internal/discord"
"github.com/dwarvesf/teleport-discord-bot/internal/httpserver"
repo "github.com/dwarvesf/teleport-discord-bot/internal/repository"
"github.com/dwarvesf/teleport-discord-bot/internal/teleport"
)

Expand All @@ -21,6 +22,8 @@ func main() {
os.Exit(1)
}

repo.ConnectDatabase()

// Create HTTP server
httpServer := httpserver.NewServer(cfg.Port)
defer func() {
Expand Down
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/russellhaering/gosaml2 v0.9.1 // indirect
Expand All @@ -46,11 +52,14 @@ require (
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/postgres v1.5.11 // indirect
gorm.io/gorm v1.25.12 // indirect
)
19 changes: 19 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/gtuk/discordwebhook v1.2.0 h1:7+gWPKSGyXjopu/6X9+oGbn0knTkDVXUM909+IXGZ/U=
github.com/gtuk/discordwebhook v1.2.0/go.mod h1:U3LdXNJ1e0bx3MMe2a4mB1VBantPHOPly2jNd8ZWXec=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
Expand Down Expand Up @@ -133,6 +145,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/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=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down Expand Up @@ -208,6 +221,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -299,5 +314,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
63 changes: 49 additions & 14 deletions internal/discord/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"time"

"github.com/dwarvesf/teleport-discord-bot/internal/config"
repo "github.com/dwarvesf/teleport-discord-bot/internal/repository"

"github.com/gravitational/teleport/api/types"
"github.com/gtuk/discordwebhook"
)
Expand Down Expand Up @@ -41,15 +43,32 @@ func ptrBool(b bool) *bool {

// HandleNewAccessRequest creates a notification for a new access request
func (d *Client) HandleNewAccessRequest(r types.AccessRequest) error {
approverIDs := ""
approvers, err := repo.GetApprovers()
if err != nil {
fmt.Printf("Failed to get approvers: %v\n", err)
} else {
for _, a := range approvers {
approverIDs += fmt.Sprintf("<@%s> ", a.DiscordID)
}
}

requester := r.GetUser()

dbRequester, err := repo.GetUserByTlpUsername(requester)
if err == nil {
requester = dbRequester.DiscordName
}

fields := []discordwebhook.Field{
{
Name: ptrString("Request ID"),
Value: ptrString(r.GetName()),
Inline: ptrBool(false),
},
{
Name: ptrString("User"),
Value: ptrString(r.GetUser()),
Name: ptrString("Requester"),
Value: ptrString(requester),
Inline: ptrBool(true),
},
{
Expand All @@ -58,29 +77,30 @@ func (d *Client) HandleNewAccessRequest(r types.AccessRequest) error {
Inline: ptrBool(true),
},
{
Name: ptrString("Session TTL"),
Name: ptrString("Duration"),
Value: ptrString(r.GetSessionTLL().Sub(r.GetCreationTime()).Round(time.Second).String()),
Inline: ptrBool(true),
},
}

if r.GetRequestReason() != "" {
fields = append(fields, discordwebhook.Field{
Name: ptrString("Request Reason"),
Name: ptrString("Reason"),
Value: ptrString(r.GetRequestReason()),
Inline: ptrBool(false),
})
}

embed := discordwebhook.Embed{
Title: ptrString("New Access Request"),
Description: ptrString(fmt.Sprintf("Approve request by running command %s: ```tctl requests approve %s```", d.cfg.WatcherList, r.GetName())),
Title: ptrString("New access request"),
Description: ptrString(fmt.Sprintf("Approve request by running command: ```tctl requests approve %s```", r.GetName())),
Color: ptrString("3093206"),
Fields: &fields,
}

message := discordwebhook.Message{
Embeds: &[]discordwebhook.Embed{embed},
Embeds: &[]discordwebhook.Embed{embed},
Content: ptrString(approverIDs),
}

if err := d.sendWebhookNotification(message); err != nil {
Expand All @@ -92,18 +112,26 @@ func (d *Client) HandleNewAccessRequest(r types.AccessRequest) error {

// HandleApproveAccessRequest creates a notification for an approved access request
func (d *Client) HandleApproveAccessRequest(r types.AccessRequest) error {
requester := r.GetUser()

dbRequester, err := repo.GetUserByTlpUsername(requester)
if err == nil {
requester = dbRequester.DiscordName
}

embed := discordwebhook.Embed{
Title: ptrString("Access Request Approved"),
Color: ptrString("2021216"),
Title: ptrString("Access request approved"),
Description: ptrString(fmt.Sprintf("Locking user by running command: ```tctl lock --user=%s --message=\"block reason\" --ttl=1h```", r.GetUser())),
Color: ptrString("2021216"),
Fields: &[]discordwebhook.Field{
{
Name: ptrString("Request ID"),
Value: ptrString(r.GetName()),
Inline: ptrBool(false),
},
{
Name: ptrString("User"),
Value: ptrString(r.GetUser()),
Name: ptrString("Requester"),
Value: ptrString(requester),
Inline: ptrBool(true),
},
{
Expand All @@ -127,8 +155,15 @@ func (d *Client) HandleApproveAccessRequest(r types.AccessRequest) error {

// HandleDenyAccessRequest creates a notification for a denied access request
func (d *Client) HandleDenyAccessRequest(r types.AccessRequest) error {
requester := r.GetUser()

dbRequester, err := repo.GetUserByTlpUsername(requester)
if err == nil {
requester = dbRequester.DiscordName
}

embed := discordwebhook.Embed{
Title: ptrString("Access Request Denied"),
Title: ptrString("Access request denied"),
Color: ptrString("15158332"),
Fields: &[]discordwebhook.Field{
{
Expand All @@ -137,8 +172,8 @@ func (d *Client) HandleDenyAccessRequest(r types.AccessRequest) error {
Inline: ptrBool(false),
},
{
Name: ptrString("User"),
Value: ptrString(r.GetUser()),
Name: ptrString("Requester"),
Value: ptrString(requester),
Inline: ptrBool(true),
},
{
Expand Down
31 changes: 31 additions & 0 deletions internal/repository/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package repo

import (
"fmt"
"log"
"os"

"gorm.io/driver/postgres"
"gorm.io/gorm"
)

var DB *gorm.DB

func ConnectDatabase() {
dbURL := os.Getenv("DB_URL")
if dbURL == "" {
log.Fatal("DB_URL environment variable is not set")
}

database, err := gorm.Open(postgres.Open(dbURL), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database: ", err)
}

DB = database
fmt.Println("Successfully connected to database!")
}

func GetDB() *gorm.DB {
return DB
}
35 changes: 35 additions & 0 deletions internal/repository/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// user.go
package repo

// User represents the user table schema
type User struct {
TlpUsername string `gorm:"column:tlp_username"`
DiscordID string `gorm:"column:discord_id;primaryKey"`
DiscordName string `gorm:"column:discord_name"`
IsApprover bool `gorm:"column:is_approver"`
}

// TableName overrides the table name
func (User) TableName() string {
return "user"
}

// GetApprovers returns all users with is_approver = true
func GetApprovers() ([]User, error) {
var approvers []User
result := DB.Where("is_approver = ?", true).Find(&approvers)
if result.Error != nil {
return nil, result.Error
}
return approvers, nil
}

// GetUserByTlpUsername returns a user by their TLP username
func GetUserByTlpUsername(tlpUsername string) (*User, error) {
var user User
result := DB.Where("tlp_username = ?", tlpUsername).First(&user)
if result.Error != nil {
return nil, result.Error
}
return &user, nil
}

0 comments on commit b7aad9a

Please sign in to comment.