Contains the Concourse pipeline definition for building a line-server container
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
2.5 KiB

  1. package main
  2. import (
  3. "bufio"
  4. "encoding/base64"
  5. "log"
  6. "net/http"
  7. "os"
  8. "strings"
  9. "golang.org/x/crypto/scrypt"
  10. )
  11. const (
  12. authPrefix = "Linx "
  13. scryptSalt = "linx-server"
  14. scryptN = 16384
  15. scryptr = 8
  16. scryptp = 1
  17. scryptKeyLen = 32
  18. )
  19. type AuthOptions struct {
  20. AuthFile string
  21. UnauthMethods []string
  22. }
  23. type auth struct {
  24. successHandler http.Handler
  25. failureHandler http.Handler
  26. authKeys []string
  27. o AuthOptions
  28. }
  29. func readAuthKeys(authFile string) []string {
  30. var authKeys []string
  31. f, err := os.Open(authFile)
  32. if err != nil {
  33. log.Fatal("Failed to open authfile: ", err)
  34. }
  35. defer f.Close()
  36. scanner := bufio.NewScanner(f)
  37. for scanner.Scan() {
  38. authKeys = append(authKeys, scanner.Text())
  39. }
  40. err = scanner.Err()
  41. if err != nil {
  42. log.Fatal("Scanner error while reading authfile: ", err)
  43. }
  44. return authKeys
  45. }
  46. func checkAuth(authKeys []string, decodedAuth []byte) (result bool, err error) {
  47. checkKey, err := scrypt.Key([]byte(decodedAuth), []byte(scryptSalt), scryptN, scryptr, scryptp, scryptKeyLen)
  48. if err != nil {
  49. return
  50. }
  51. encodedKey := base64.StdEncoding.EncodeToString(checkKey)
  52. for _, v := range authKeys {
  53. if encodedKey == v {
  54. result = true
  55. return
  56. }
  57. }
  58. result = false
  59. return
  60. }
  61. func (a auth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  62. if sliceContains(a.o.UnauthMethods, r.Method) {
  63. // allow unauthenticated methods
  64. a.successHandler.ServeHTTP(w, r)
  65. return
  66. }
  67. authHeader := r.Header.Get("Authorization")
  68. if !strings.HasPrefix(authHeader, authPrefix) {
  69. a.failureHandler.ServeHTTP(w, r)
  70. return
  71. }
  72. decodedAuth, err := base64.StdEncoding.DecodeString(authHeader[len(authPrefix):])
  73. if err != nil {
  74. a.failureHandler.ServeHTTP(w, r)
  75. return
  76. }
  77. result, err := checkAuth(a.authKeys, decodedAuth)
  78. if err != nil || !result {
  79. a.failureHandler.ServeHTTP(w, r)
  80. return
  81. }
  82. a.successHandler.ServeHTTP(w, r)
  83. }
  84. func UploadAuth(o AuthOptions) func(http.Handler) http.Handler {
  85. fn := func(h http.Handler) http.Handler {
  86. return auth{
  87. successHandler: h,
  88. failureHandler: http.HandlerFunc(badAuthorizationHandler),
  89. authKeys: readAuthKeys(o.AuthFile),
  90. o: o,
  91. }
  92. }
  93. return fn
  94. }
  95. func badAuthorizationHandler(w http.ResponseWriter, r *http.Request) {
  96. w.WriteHeader(http.StatusUnauthorized)
  97. http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
  98. }
  99. func sliceContains(slice []string, s string) bool {
  100. for _, v := range slice {
  101. if s == v {
  102. return true
  103. }
  104. }
  105. return false
  106. }