2024-12-18 11:08:21 -04:00

187 lines
5.3 KiB
Go

package auth
import (
"context"
"fmt"
"log/slog"
"slices"
"strconv"
"strings"
"git.maximotejeda.com/maximo/cedulados-bot/config"
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type Auth struct {
bot *tgbotapi.BotAPI
update *tgbotapi.Update
user *tgbotapi.User
uSVC ports.UserService
log *slog.Logger
ctx context.Context
}
func NewAuth(ctx context.Context, log *slog.Logger, bot *tgbotapi.BotAPI, update *tgbotapi.Update, user *tgbotapi.User, uSVC ports.UserService) *Auth {
a := &Auth{
ctx: ctx,
log: log,
user: user,
bot: bot,
uSVC: uSVC,
update: update,
}
return a
}
func (a *Auth) Authenticate() bool {
switch IsUserAdmin(a.user.ID) {
case true:
// theres an user env admin
// check if user exist on db
_, err := a.uSVC.Get(a.user.ID)
if err != nil {
a.log.Error("geting user", "error", err)
a.log.Debug("user seems to not exists")
if strings.Contains(err.Error(), "sql: no rows in result set") {
// if user does not exist create it
_, err := a.uSVC.Create(a.user)
a.log.Debug("creating user")
if err != nil {
a.log.Error("creating new user for admnin", "err", err)
}
// add bot to user list
_, err = a.uSVC.AddBot(a.user.ID, a.bot.Self.UserName)
if err != nil {
a.log.Error("Adding bot to admin user list", "err", err)
}
}
}
return true
case false:
// user is not admin and need to be authorized
// check if user is on db
_, err := a.uSVC.Get(a.update.SentFrom().ID)
if err != nil {
// user need auth
a.log.Error("user not in db, authorization from an admin is required", "error", err)
// add user to manage it
if strings.Contains(err.Error(), "sql: no rows in result set") {
// check if theres an access request from the same user and bot
_, err := a.uSVC.Create(a.user)
if err != nil {
a.log.Error("creating new user", "user", a.user.ID, "error", err)
}
// create access request
_, err = a.uSVC.CreateAccessRequest(a.user.ID, a.bot.Self.UserName)
if err != nil {
a.log.Error("creating access request for ", "user", a.user.ID, "error", err)
}
}
} else {
bot, err := a.uSVC.GetBots(a.user.ID)
if err != nil {
a.log.Error("checking bots on user access")
}
switch HasUserAccess(bot, a.bot.Self.UserName) {
case true:
return true
case false:
// check for banned user
buser, err := a.uSVC.GetAllBannedUsers(a.bot.Self.UserName)
if err != nil {
a.log.Error("error querying banned user")
}
for _, u := range buser.GetBans(){
if u.TgbId == a.update.SentFrom().ID{
msg := tgbotapi.NewMessage(u.TgbId, "user access is restricted, please ask for permission")
a.bot.Send(msg)
return false
}
}
ac, err := a.uSVC.GetAccessRequest(a.user.ID)
acl := []string{}
for _, val := range ac.Access {
acl = append(acl, val.BotName)
}
if slices.Contains(acl, a.bot.Self.UserName) {
// create access request
a.log.Info("Access Request found returning early", "user", a.user.ID, "error", err)
return false
} else {
// create one
_, err = a.uSVC.CreateAccessRequest(a.user.ID, a.bot.Self.UserName)
if err != nil {
a.log.Error("creating access request", "err", err)
}
}
}
}
}
// get all admins
userL, _ := GetAdminFromEnv()
// send a mesage to all admins
for _, adm := range userL {
msg := GenerateAccessRequestMessage(a.update.SentFrom(), adm, a.bot.Self.UserName)
a.bot.Send(msg)
}
return false
}
// GetAdminFromEnv
// will get an env variable that is a list of tgbID comma separated
// if the user trying to enter is admin auth the user
func GetAdminFromEnv() (adminList []int64, errList []error) {
adminsStrList := config.GetAdminsList()
list := strings.Split(adminsStrList, ",")
adminList = []int64{}
errList = []error{}
for _, item := range list {
adm, err := strconv.ParseInt(item, 10, 64)
if err != nil {
err = fmt.Errorf("parsing tgb admin id: %s\n err: %w", item, err)
fmt.Println(err)
errList = append(errList, err)
continue
}
adminList = append(adminList, adm)
}
return
}
// IsUserAdmin
// check if userID is admin on bot
func IsUserAdmin(userID int64) bool {
userL, errl := GetAdminFromEnv()
if len(errl) > 0 {
fmt.Printf("error no admin in var %v", errl)
}
return slices.Contains(userL, userID)
}
func HasUserAccess(bots []string, botName string) bool {
return slices.Contains(bots, botName)
}
func GenerateAccessRequestMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig {
txt := fmt.Sprintf(`User %s is requesting access
ID: %d
FirstName: %s
LastName: %s
ChatID: %d
`, up.UserName, up.ID, up.FirstName, up.LastName, up.ID)
msg := tgbotapi.NewMessage(adm, txt)
keyboard := tgbotapi.InlineKeyboardMarkup{}
row := tgbotapi.NewInlineKeyboardRow()
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Grant", fmt.Sprintf("operation=grant&userID=%d&bot=%s", up.ID, bn)))
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Deny", fmt.Sprintf("operation=deny&userID=%d&bot=%s", up.ID, bn)))
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn)))
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row)
msg.ReplyMarkup = keyboard
return &msg
}