-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 693c397
Showing
5 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MaiLux | ||
======= | ||
Mailux is a microservice designed to recieve mails on temporarily inboxes so the user can send emails from his mail to the temp-email we generated before, so you as a developer can login him without password! | ||
|
||
Flow | ||
===== | ||
1- You request the `/mailbox/generate/<email@address.here>`. | ||
2- The user send an email to the mailbox generated by the above step. | ||
3- You hit the `/mailbox/info/<mailboxID>` each X of time (from the browser). | ||
4- if the above step returned that it has been received, just send a request to the backend to hit the above endpoint again just as a confirmation, after that you respond to the browser with a `token/session`. | ||
|
||
Help | ||
==== | ||
```bash | ||
# have fun with this! | ||
$ mailux --help | ||
``` | ||
|
||
Credits | ||
======= | ||
Mohamed Al Ashaal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/base64" | ||
"log" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
"github.com/labstack/echo" | ||
"github.com/labstack/echo/middleware" | ||
) | ||
|
||
func initHTTP() { | ||
e := echo.New() | ||
e.HideBanner = true | ||
|
||
e.Pre(middleware.RemoveTrailingSlash()) | ||
e.Use(middleware.Recover()) | ||
e.Use(middleware.Logger()) | ||
e.Use(middleware.CORS()) | ||
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{Level: 9})) | ||
e.Use(middleware.BodyLimit("100K")) | ||
|
||
e.GET("/", func(c echo.Context) error { | ||
return c.JSON(200, map[string]interface{}{ | ||
"success": true, | ||
"message": "let's reverse the login process", | ||
}) | ||
}) | ||
|
||
/** | ||
* Get the information of an inbox (temp-inbox) | ||
*/ | ||
e.GET("/inbox/info/:inboxId", func(c echo.Context) error { | ||
id := c.Param("inboxId") | ||
return c.JSON(200, map[string]interface{}{ | ||
"success": true, | ||
"data": map[string]interface{}{ | ||
"uuid": id, | ||
"inbox": id + "@" + *flagDomain, | ||
"status": strings.ToLower(redisClient.Get(redisKeyPrefix(id+":status")).Val()) == "done", | ||
"email": redisClient.Get(redisKeyPrefix(id + ":email")).Val(), | ||
"expires": redisClient.Get(redisKeyPrefix(id + ":expires")).Val(), | ||
}, | ||
}) | ||
}) | ||
|
||
/** | ||
* Generates a temp-inbox for the specified email | ||
*/ | ||
e.GET("/inbox/generate/:email", func(c echo.Context) error { | ||
id := uuid.New().String() | ||
if c.QueryParam("suffix") != "" { | ||
id += "-" + c.QueryParam("suffix") | ||
} | ||
id = base64.RawStdEncoding.EncodeToString([]byte(id)) | ||
ttl, _ := strconv.Atoi(c.QueryParam("ttl")) | ||
if ttl < 1 { | ||
ttl = *flagDefaultTTL | ||
} | ||
exp := time.Now().Unix() + int64(ttl) | ||
email := strings.ToLower(c.Param("email")) | ||
redisDur := time.Second * 3600 | ||
|
||
redisClient.Set(redisKeyPrefix(email+":inbox"), id, redisDur).Val() | ||
redisClient.Set(redisKeyPrefix(id+":status"), "pending", redisDur).Val() | ||
redisClient.Set(redisKeyPrefix(id+":email"), email, redisDur).Val() | ||
redisClient.Set(redisKeyPrefix(id+":expires"), exp, redisDur).Val() | ||
|
||
return c.JSON(200, map[string]interface{}{ | ||
"success": true, | ||
"data": map[string]interface{}{ | ||
"uuid": id, | ||
"inbox": id + "@" + *flagDomain, | ||
"status": false, | ||
"email": email, | ||
"expires": exp, | ||
}, | ||
}) | ||
}) | ||
|
||
log.Fatal(e.Start(*flagHTTPAddr)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package main | ||
|
||
func main() { | ||
go initHTTP() | ||
go initSMTP() | ||
|
||
select {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
"net" | ||
"strings" | ||
"time" | ||
|
||
"github.com/alash3al/go-mailbox" | ||
"github.com/zaccone/spf" | ||
) | ||
|
||
func initSMTP() { | ||
smtp.HandleFunc("*@"+*flagDomain, func(req *smtp.Envelope) error { | ||
ipAddr, _, err := net.SplitHostPort(req.RemoteAddr) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fromEmail, toEmail := req.MessageFrom, req.MessageTo | ||
fromEmail = strings.ToLower(fromEmail) | ||
_, fromDomain, err := smtp.SplitAddress(fromEmail) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
res, _, err := spf.CheckHost(net.ParseIP(ipAddr), fromDomain, fromEmail) | ||
if (err != nil || res != spf.Pass) && *flagSPFChecker { | ||
return errors.New("ERR_CHEATING_DETECTED") | ||
} | ||
|
||
inbox := strings.Split(toEmail, "@")[0] | ||
expires, _ := redisClient.Get(redisKeyPrefix(inbox + ":expires")).Int64() | ||
if time.Now().Unix() >= expires { | ||
return errors.New("ERR_EXPIRED") | ||
} | ||
|
||
if strings.ToLower(fromEmail) != redisClient.Get(redisKeyPrefix(inbox+":email")).Val() { | ||
return errors.New("ERR_MISS_MATCH") | ||
} | ||
|
||
if strings.ToLower(redisClient.Get(redisKeyPrefix(inbox+":status")).Val()) == "pending" { | ||
ttl := redisClient.TTL(redisKeyPrefix(inbox + ":status")).Val() | ||
redisClient.Set(redisKeyPrefix(inbox+":status"), "done", ttl).Val() | ||
} | ||
|
||
return nil | ||
}) | ||
|
||
log.Fatal(smtp.ListenAndServe(*flagSMTPAddr, nil)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
|
||
"github.com/go-redis/redis" | ||
) | ||
|
||
var ( | ||
flagSMTPAddr = flag.String("smtp", ":25025", "the smtp (inbox) address to listen on") | ||
flagHTTPAddr = flag.String("http", ":5050", "the http server address to listen on") | ||
flagSPFChecker = flag.Bool("spf", false, "whether to enable SPF checking or not") | ||
flagRedis = flag.String("redis", "redis://localhost:6379/1", "redis url") | ||
flagDefaultTTL = flag.Int("ttl", 30, "the time-to-live of each auth-inbox in seconds") | ||
flagDomain = flag.String("domain", "local.host", "the default domain name of the server") | ||
) | ||
|
||
var ( | ||
redisClient *redis.Client | ||
|
||
redisKeyPrefix = func(k string) string { | ||
return "mailux:" + k | ||
} | ||
) | ||
|
||
func init() { | ||
flag.Parse() | ||
|
||
opt, err := redis.ParseURL(*flagRedis) | ||
if err != nil { | ||
log.Fatal("[Redis]", " ", err.Error()) | ||
} | ||
|
||
redisClient = redis.NewClient(opt) | ||
if _, err := redisClient.Ping().Result(); err != nil { | ||
log.Fatal("[Redis]", " ", err.Error()) | ||
} | ||
} |