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 }