feat(misc): allows user edition, templating of the home, and more
All checks were successful
build / build (push) Successful in 2m9s
All checks were successful
build / build (push) Successful in 2m9s
This commit is contained in:
parent
09d3149932
commit
d08f82ba66
8 changed files with 144 additions and 15 deletions
11
README.md
11
README.md
|
@ -40,8 +40,9 @@ For authentication, you want to change your `/etc/dovecot/dovecot-sql.conf.ext`
|
|||
```
|
||||
driver = pgsql
|
||||
connect = host=YOURHOST port=5432 user=YOURUSER password=APASSWORD dbname=DBNAME
|
||||
password_query = SELECT password, username AS user FROM users WHERE username = '%n' AND domain = '%d'
|
||||
user_query = SELECT maildir, 1000 AS uid, 1000 AS gid FROM users WHERE username = '%n' AND domain = '%d' AND active = '1'
|
||||
|
||||
password_query = SELECT concat(username, '@', domain) AS user, password FROM users WHERE username = '%n' AND domain = '%d' AND active = true
|
||||
user_query = SELECT home, 1000 AS uid, 1000 AS gid FROM users WHERE username = '%n' AND domain = '%d' AND active = true
|
||||
```
|
||||
|
||||
### OpenDKIM
|
||||
|
@ -60,6 +61,12 @@ postgres:
|
|||
user: postgres
|
||||
password: postgres123
|
||||
sslmode: disable
|
||||
defaults:
|
||||
provider: ovh
|
||||
# this is used to create the `home` field of the user.
|
||||
# in systems with virtual mail this corresponds to the physical
|
||||
# location of the vmail directory on your host
|
||||
homeTemplate: "/var/lib/vmail/{{ .Domain }}/{{ .Username }}"
|
||||
providers:
|
||||
ovh:
|
||||
application_key: <CHANGEME>
|
||||
|
|
|
@ -5,7 +5,12 @@ postgres:
|
|||
user: postgres
|
||||
password: postgres123
|
||||
sslmode: disable
|
||||
defaultProvider: ovh
|
||||
defaults:
|
||||
provider: ovh
|
||||
# this is used to create the `home` field of the user.
|
||||
# in systems with virtual mail this corresponds to the physical
|
||||
# location of the vmail directory on your host
|
||||
homeTemplate: "/var/lib/vmail/{{ .Domain }}/{{ .Username }}"
|
||||
providers:
|
||||
ovh:
|
||||
application_key: <CHANGEME>
|
||||
|
|
|
@ -27,10 +27,10 @@ var DKIMKeyCmd = &cobra.Command{
|
|||
Short: "manages DKIM keys",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if flagProvider == "" {
|
||||
if cfg.DefaultProvider == "" {
|
||||
if cfg.Defaults.Provider == "" {
|
||||
logrus.Fatal("no provider specified and no default provider in config, aborting")
|
||||
}
|
||||
flagProvider = cfg.DefaultProvider
|
||||
flagProvider = cfg.Defaults.Provider
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -240,7 +240,7 @@ var DKIMKeyPublishCmd = &cobra.Command{
|
|||
|
||||
pv := flagProvider
|
||||
if flagProvider == "" {
|
||||
pv = cfg.DefaultProvider
|
||||
pv = cfg.Defaults.Provider
|
||||
if pv == "" {
|
||||
logrus.Fatal("no provider specified")
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ var DKIMKeyUnpublishCmd = &cobra.Command{
|
|||
|
||||
pv := flagProvider
|
||||
if flagProvider == "" {
|
||||
pv = cfg.DefaultProvider
|
||||
pv = cfg.Defaults.Provider
|
||||
if pv == "" {
|
||||
logrus.Fatal("no provider specified")
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ var RootCmd = &cobra.Command{
|
|||
func InitRootCmd() {
|
||||
InitUserCmd()
|
||||
InitDKIMKeyCmd()
|
||||
InitTestCmd()
|
||||
|
||||
RootCmd.AddCommand(VersionCmd)
|
||||
RootCmd.AddCommand(InitDBCmd)
|
||||
|
|
|
@ -11,6 +11,11 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
flagTestMessage string
|
||||
flagTestMessageSubject string
|
||||
)
|
||||
|
||||
var TestCmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "sends an email through the configured server",
|
||||
|
@ -39,7 +44,7 @@ var TestCmd = &cobra.Command{
|
|||
headers := make(map[string]string)
|
||||
headers["From"] = cfg.Test.Username
|
||||
headers["To"] = args[0]
|
||||
headers["Subject"] = "This is a test email from the command line"
|
||||
headers["Subject"] = flagTestMessageSubject
|
||||
|
||||
message := ""
|
||||
for k, v := range headers {
|
||||
|
@ -47,7 +52,7 @@ var TestCmd = &cobra.Command{
|
|||
}
|
||||
message += "\r\n"
|
||||
|
||||
message += "This is a test email message sent through the command line utility."
|
||||
message += flagTestMessage
|
||||
|
||||
auth := smtp.PlainAuth("", cfg.Test.Username, cfg.Test.Password, strings.Split(cfg.Test.Address, ":")[0])
|
||||
|
||||
|
@ -83,3 +88,8 @@ var TestCmd = &cobra.Command{
|
|||
logrus.Info("sent test email")
|
||||
},
|
||||
}
|
||||
|
||||
func InitTestCmd() {
|
||||
TestCmd.PersistentFlags().StringVarP(&flagTestMessage, "message", "m", "This is a test email message sent through the command line utility.", "What to send as a test message")
|
||||
TestCmd.PersistentFlags().StringVarP(&flagTestMessageSubject, "subject", "s", "This is a test email from the command line", "What to send as a test message subject")
|
||||
}
|
||||
|
|
107
pkg/cmd/user.go
107
pkg/cmd/user.go
|
@ -1,7 +1,9 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"git.maurice.fr/thomas/mailout/pkg/database"
|
||||
"git.maurice.fr/thomas/mailout/pkg/models"
|
||||
|
@ -13,6 +15,11 @@ import (
|
|||
|
||||
var (
|
||||
flagUserActive bool
|
||||
flagUserHome string
|
||||
flagUserQuota int64
|
||||
flagUserPassword string
|
||||
flagUserGID int
|
||||
flagUserUID int
|
||||
)
|
||||
|
||||
var UserCmd = &cobra.Command{
|
||||
|
@ -73,6 +80,24 @@ var UserAddCmd = &cobra.Command{
|
|||
Active: flagUserActive,
|
||||
Password: fmt.Sprintf("{BLF-CRYPT}%s", string(passwordHash)),
|
||||
}
|
||||
|
||||
if flagUserHome == "" {
|
||||
flagUserHome = cfg.Defaults.HomeTemplate
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Parse(flagUserHome)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not parse the default home template")
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
err = tmpl.Execute(buf, user)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not render template")
|
||||
}
|
||||
|
||||
user.Home = buf.String()
|
||||
|
||||
err = db.Save(&user).Error
|
||||
|
||||
if err != nil {
|
||||
|
@ -100,10 +125,10 @@ var UserListCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
tData := pterm.TableData{
|
||||
{"id", "username", "domain", "active", "uid", "gid", "created_at", "updated_at"},
|
||||
{"id", "username", "domain", "active", "uid", "gid", "home"},
|
||||
}
|
||||
for _, u := range qRes {
|
||||
tData = append(tData, []string{u.ID.String(), u.Username, u.Domain, fmt.Sprintf("%v", u.Active), fmt.Sprintf("%d", u.UID), fmt.Sprintf("%d", u.GID), u.CreatedAt.String(), u.UpdatedAt.String()})
|
||||
tData = append(tData, []string{u.ID.String(), u.Username, u.Domain, fmt.Sprintf("%v", u.Active), fmt.Sprintf("%d", u.UID), fmt.Sprintf("%d", u.GID), u.Home})
|
||||
}
|
||||
|
||||
pterm.DefaultTable.WithHasHeader().WithData(tData).Render()
|
||||
|
@ -182,12 +207,90 @@ var UserDeactivateCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var UserEditCmd = &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "edites a user",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
db, err := database.NewDB(cfg)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not connect to the database")
|
||||
}
|
||||
|
||||
userQuery, err := buildUserQuery(args[0])
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("unable to determine user")
|
||||
}
|
||||
|
||||
qRes := make([]models.User, 0)
|
||||
|
||||
err = db.Model(&models.User{}).Find(&qRes, userQuery).Error
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not preload user")
|
||||
}
|
||||
|
||||
if len(qRes) == 0 {
|
||||
logrus.Fatal("no such user")
|
||||
}
|
||||
|
||||
user := qRes[0]
|
||||
|
||||
if flagUserHome != "" {
|
||||
tmpl, err := template.New("").Parse(flagUserHome)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not parse the default home template")
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
err = tmpl.Execute(buf, user)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not render home template")
|
||||
}
|
||||
|
||||
user.Home = buf.String()
|
||||
}
|
||||
|
||||
if flagUserPassword != "" {
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(args[1]), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not compute user password hash")
|
||||
}
|
||||
|
||||
user.Password = fmt.Sprintf("{BLF-CRYPT}%s", string(passwordHash))
|
||||
}
|
||||
|
||||
if flagUserGID != -1 {
|
||||
user.GID = flagUserGID
|
||||
}
|
||||
|
||||
if flagUserUID != -1 {
|
||||
user.UID = flagUserUID
|
||||
}
|
||||
|
||||
err = db.Save(&user).Error
|
||||
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not update user")
|
||||
}
|
||||
|
||||
logrus.Infof("edited user %s", args[0])
|
||||
},
|
||||
}
|
||||
|
||||
func InitUserCmd() {
|
||||
UserCmd.AddCommand(UserAddCmd)
|
||||
UserCmd.AddCommand(UserEditCmd)
|
||||
UserCmd.AddCommand(UserListCmd)
|
||||
UserCmd.AddCommand(UserDeleteCmd)
|
||||
UserCmd.AddCommand(UserActivateCmd)
|
||||
UserCmd.AddCommand(UserDeactivateCmd)
|
||||
|
||||
UserAddCmd.PersistentFlags().BoolVarP(&flagUserActive, "active", "a", true, "whether or not the created user is active")
|
||||
UserAddCmd.PersistentFlags().StringVarP(&flagUserHome, "home", "", "", "template to use for user's home directories")
|
||||
|
||||
UserEditCmd.PersistentFlags().StringVarP(&flagUserPassword, "password", "p", "", "User password")
|
||||
UserEditCmd.PersistentFlags().StringVarP(&flagUserHome, "home", "", "", "home (user's mailbox)")
|
||||
UserEditCmd.PersistentFlags().Int64VarP(&flagUserQuota, "quota", "q", -1, "Quota in bytes for the user")
|
||||
UserEditCmd.PersistentFlags().IntVarP(&flagUserUID, "uid", "u", -1, "user's uid")
|
||||
UserEditCmd.PersistentFlags().IntVarP(&flagUserGID, "gid", "g", -1, "user's gid")
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ type Config struct {
|
|||
Database string `yaml:"database"`
|
||||
SSLMode string `yaml:"sslmode"`
|
||||
} `yaml:"postgres"`
|
||||
DefaultProvider string `yaml:"defaultProvider"`
|
||||
Defaults struct {
|
||||
HomeTemplate string `yaml:"homeTemplate"`
|
||||
Provider string `yaml:"provider"`
|
||||
} `yaml:"defaults"`
|
||||
Providers struct {
|
||||
OVH *providerConfigs.OVHConfig `yaml:"ovh"`
|
||||
} `yaml:"providers"`
|
||||
|
|
Loading…
Add table
Reference in a new issue