From 5b1a41c86ebfa527d27eb721d86f59c85d8a3487 Mon Sep 17 00:00:00 2001 From: viktorbarzin Date: Thu, 8 Apr 2021 22:12:45 +0100 Subject: [PATCH] allow infra cli to add email aliases --- cli/email_alias.go | 81 +++++++++++++++++++ cli/go.mod | 2 + cli/go.sum | 4 + cli/main.go | 34 ++++++++ .../kubernetes/mailserver/extra/aliases.txt | 6 -- 5 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 cli/email_alias.go diff --git a/cli/email_alias.go b/cli/email_alias.go new file mode 100644 index 00000000..04c2a0c5 --- /dev/null +++ b/cli/email_alias.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/badoux/checkmail" + "github.com/brianvoe/gofakeit/v6" + "github.com/golang/glog" + "github.com/pkg/errors" +) + +const ( + addEmailAliasUseCase = "add-email-alias" + emailAliasFlagName = "forward-to" + fromEmailDomainFlagName = "from-domain" + emailAliasesConfigFileRelative = "/modules/kubernetes/mailserver/extra/aliases.txt" +) + +func addEmailAlias(gitFs *GitFS, to, fromDomain string) (string, error) { + if err := checkmail.ValidateFormat(to); err != nil { + return "", errors.Wrapf(err, fmt.Sprintf("failed to create new email aliases because invalid input format: %s", to)) + } + if err := checkmail.ValidateHost(to); err != nil { + return "", errors.Wrapf(err, fmt.Sprintf("failed to create new email aliases because domain for %s does not exist", to)) + } + aliasEmail := generateRandomEmail(fromDomain) + glog.Infof("Adding %s -> %s alias to %s", aliasEmail, to, emailAliasesConfigFileRelative) + contents := fmt.Sprintf("%s %s", aliasEmail, to) + + // Read existing contents + fRead, err := (*gitFs.fs).OpenFile(emailAliasesConfigFileRelative, os.O_RDONLY, 0644) + if err != nil { + return "", errors.Wrapf(err, "failed to open file where email aliases are recorded") + } + fileContentsBytes, err := ioutil.ReadAll(fRead) + if err != nil { + return "", errors.Wrapf(err, "failed to read existing aliases file") + } + defer fRead.Close() + newContents := getAddedAliasContents(string(fileContentsBytes), aliasEmail, to) + + // Write new contents + fWrite, err := (*gitFs.fs).OpenFile(emailAliasesConfigFileRelative, os.O_WRONLY, 0644) + if err != nil { + return "", errors.Wrapf(err, "failed to open file where new email alias will be added") + } + defer fWrite.Close() + + glog.Infof("writing new contents to file: %s", newContents) + fRead.Write([]byte(newContents)) + + if _, err = fWrite.Write([]byte(contents)); err != nil { + return "", errors.Wrapf(err, "failed to write config to file") + } + return aliasEmail, nil +} + +func generateRandomEmail(fromDomain string) string { + return fmt.Sprintf("%s-%s-generated%s", strings.ToLower(gofakeit.Adverb()), strings.ToLower(gofakeit.FirstName()), fromDomain) +} + +func getPostFixAlias(from, to string) string { + return fmt.Sprintf("%s %s", from, to) +} + +func getAddedAliasContents(currentContents, from, to string) string { + glog.Infof("Existingcontent: %s", currentContents) + lines := strings.Split(currentContents, "\n") + newLines := []string{} + // If `to` already has an alias, overwrite it + for _, l := range lines { + if !strings.HasSuffix(l, to) { + newLines = append(newLines, l) + } + } + newLines = append(newLines, getPostFixAlias(from, to)) + return strings.Join(newLines, "\n") +} diff --git a/cli/go.mod b/cli/go.mod index 1eb13ba8..0c459004 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -3,6 +3,8 @@ module viktorbarzin/infra/cli go 1.16 require ( + github.com/badoux/checkmail v1.2.1 // indirect + github.com/brianvoe/gofakeit/v6 v6.3.0 // indirect github.com/go-git/go-billy/v5 v5.1.0 // indirect github.com/go-git/go-git/v5 v5.3.0 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect diff --git a/cli/go.sum b/cli/go.sum index 6b001e42..25e0a852 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -4,6 +4,10 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/badoux/checkmail v1.2.1 h1:TzwYx5pnsV6anJweMx2auXdekBwGr/yt1GgalIx9nBQ= +github.com/badoux/checkmail v1.2.1/go.mod h1:XroCOBU5zzZJcLvgwU15I+2xXyCdTWXyR9MGfRhBYy0= +github.com/brianvoe/gofakeit/v6 v6.3.0 h1:h1M5XPubl81K+41Ry0g5P4Q9a7OCM8FgFf2Heey5j24= +github.com/brianvoe/gofakeit/v6 v6.3.0/go.mod h1:palrJUk4Fyw38zIFB/uBZqsgzW5VsNllhHKKwAebzew= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/cli/main.go b/cli/main.go index 81b9ebab..3e8a75f4 100644 --- a/cli/main.go +++ b/cli/main.go @@ -42,6 +42,10 @@ func run() error { // OpenWRT DNS flags openWRTNewDNS := flag.String(setupOpenWRTNewDNSFlagName, "", fmt.Sprintf("New DNS server to set.")) + // add email alias flags + emailToForwardTo := flag.String(emailAliasFlagName, "", "Email which is used to forward emails to.") + fromDomain := flag.String(fromEmailDomainFlagName, "@viktorbarzin.me", "Domain name which will receive emails. Example @viktorbarzin.me") + // Flag definitions above! flag.Parse() @@ -115,6 +119,36 @@ func run() error { if *printResultOnly { println(fmt.Sprintf("Successfully set DNS server to '%s'", *openWRTNewDNS)) } + case addEmailAliasUseCase: + if *emailToForwardTo == "" { + return fmt.Errorf("%s must not be empty when using %s use case", emailAliasFlagName, addEmailAliasUseCase) + } + glog.Infof("Trying to add %s email alias", *emailToForwardTo) + gitFs, err := NewGitFS(repository) + if err != nil { + return errors.Wrapf(err, "failed to initialize git fs") + } + worktree, err := gitFs.repo.Worktree() + if err != nil { + return errors.Wrapf(err, "failed to get worktree") + } + emailAlias, err := addEmailAlias(gitFs, *emailToForwardTo, *fromDomain) + if err != nil { + return errors.Wrapf(err, "failed to add email alias") + } + glog.Infof("generated %s email alias", emailAlias) + // commit changes + if _, err = worktree.Commit("Added new email alias", &git.CommitOptions{All: true, Author: &object.Signature{Name: "Webhook Handler Bot"}}); err != nil { + return errors.Wrapf(err, "failed to commit") + } + if *printResultOnly { + fmt.Printf("Successfully created '%s' -> '%s' forwarding", emailAlias, *emailToForwardTo) + // println(ip) + } + if err = gitFs.Push(); err != nil { + return errors.Wrapf(err, "failed to push changes") + } + glog.Infof("successfully added %s -> %s email aliasing", emailAlias, *emailToForwardTo) default: err = errors.New(fmt.Sprintf("unsupported use case: %s", *useCase)) } diff --git a/modules/kubernetes/mailserver/extra/aliases.txt b/modules/kubernetes/mailserver/extra/aliases.txt index 81590219..caa26064 100644 --- a/modules/kubernetes/mailserver/extra/aliases.txt +++ b/modules/kubernetes/mailserver/extra/aliases.txt @@ -1,7 +1 @@ -utterly-breana@viktorbarzin.me me@viktorbarzin.mee me@viktorbarzin.me' will point test@ -> me@ # Add aliases here. Example: 'test@viktorbarzin.me me@viktorbarzin.me' will point test@ -> me@ - -nonetheless-tyshawn@viktorbarzin.me me@viktorbarzin.menonetheless-tyshawn@viktorbarzin.me me@viktorbarzin.me# Add aliases here. Example: 'test@viktorbarzin.me me@viktorbarzin.me' will point test@ -> me@ -# Add aliases here. Example: 'test@viktorbarzin.me me@viktorbarzin.me' will point test@ -> me@ - -instead-mertie@viktorbarzin.me me@viktorbarzin.meinstead-mertie@viktorbarzin.me me@viktorbarzin.me \ No newline at end of file