main.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "log"
  7. "net/http"
  8. "os"
  9. "time"
  10. )
  11. type userMapping struct {
  12. SFTPGoUsername string
  13. AuthyID int64
  14. AuthyAPIKey string
  15. }
  16. type checkPasswordResponse struct {
  17. // 0 KO, 1 OK, 2 partial success
  18. Status int `json:"status"`
  19. // for status == 2 this is the password that SFTPGo will check against the one stored
  20. // inside the data provider
  21. ToVerify string `json:"to_verify"`
  22. }
  23. var (
  24. mapping []userMapping
  25. )
  26. func init() {
  27. // this is for demo only, you probably want to get this mapping dynamically, for example using a database query
  28. mapping = append(mapping, userMapping{
  29. SFTPGoUsername: "<SFTPGo username>",
  30. AuthyID: 1234567,
  31. AuthyAPIKey: "<your api key>",
  32. })
  33. }
  34. func printResponse(status int, toVerify string) {
  35. r := checkPasswordResponse{
  36. Status: status,
  37. ToVerify: toVerify,
  38. }
  39. resp, _ := json.Marshal(r)
  40. fmt.Printf("%v\n", string(resp))
  41. if status > 0 {
  42. os.Exit(0)
  43. } else {
  44. os.Exit(1)
  45. }
  46. }
  47. func main() {
  48. // get credentials from env vars
  49. username := os.Getenv("SFTPGO_AUTHD_USERNAME")
  50. password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
  51. for _, m := range mapping {
  52. if m.SFTPGoUsername == username {
  53. // Authy token len is 7, we assume that we have the password followed by the token
  54. pwdLen := len(password)
  55. if pwdLen <= 7 {
  56. printResponse(0, "")
  57. }
  58. pwd := password[:pwdLen-7]
  59. authyToken := password[pwdLen-7:]
  60. // now verify the authy token and instruct SFTPGo to check the password if the token is OK
  61. url := fmt.Sprintf("https://api.authy.com/protected/json/verify/%v/%v", authyToken, m.AuthyID)
  62. req, err := http.NewRequest(http.MethodGet, url, nil)
  63. if err != nil {
  64. log.Fatal(err)
  65. }
  66. req.Header.Set("X-Authy-API-Key", m.AuthyAPIKey)
  67. httpClient := &http.Client{
  68. Timeout: 10 * time.Second,
  69. }
  70. resp, err := httpClient.Do(req)
  71. if err != nil {
  72. printResponse(0, "")
  73. }
  74. defer resp.Body.Close()
  75. if resp.StatusCode != http.StatusOK {
  76. // status code 200 is expected
  77. printResponse(0, "")
  78. }
  79. var authyResponse map[string]interface{}
  80. respBody, err := io.ReadAll(resp.Body)
  81. if err != nil {
  82. printResponse(0, "")
  83. }
  84. err = json.Unmarshal(respBody, &authyResponse)
  85. if err != nil {
  86. printResponse(0, "")
  87. }
  88. if authyResponse["success"].(string) == "true" {
  89. printResponse(2, pwd)
  90. }
  91. printResponse(0, "")
  92. break
  93. }
  94. }
  95. // no mapping found
  96. printResponse(0, "")
  97. }