Working registration
This commit is contained in:
parent
a79a4166bd
commit
57ab820693
|
@ -1,4 +1,4 @@
|
|||
ADMIN_API_KEY=changeMe
|
||||
AUTH_API_URL=http://auth-api:4000
|
||||
API_URL=http://auth-api:4000
|
||||
JWT_SHARED_SECRET=changeMe
|
||||
PORT=4001
|
1
go.mod
1
go.mod
|
@ -10,6 +10,7 @@ require (
|
|||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/klauspost/compress v1.14.3 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.33.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -135,6 +135,8 @@ github.com/gofiber/template v1.6.23 h1:rxIzrukkFrRiC22Z/WRwuySU2z09m932/RkVMAuwc
|
|||
github.com/gofiber/template v1.6.23/go.mod h1:OpKYcUcfli731QNdeN8Y/EkIdKIzN6zenwOj2JrL/pg=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
|
162
src/api/api.go
Normal file
162
src/api/api.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
// Method must be a valid REST method, like GET, POST, PUT, DELETE etc
|
||||
func (api Api) Call(method string, path string, jsonPayload []byte) (ApiRes, error) {
|
||||
respObj := ApiRes{}
|
||||
|
||||
adminToken, _, err := api.getAdminToken()
|
||||
if err != nil {
|
||||
return ApiRes{}, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, api.URL+path, bytes.NewBuffer(jsonPayload))
|
||||
if err != nil {
|
||||
api.Log.Error("Could not create request", "method", method, "err", err.Error())
|
||||
return ApiRes{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "bearer "+adminToken)
|
||||
|
||||
httpClient := &http.Client{}
|
||||
res, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not call backend API", "err", err.Error())
|
||||
return ApiRes{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
api.Log.Debug("API res status", "status", res.StatusCode)
|
||||
respObj.StatusCode = res.StatusCode
|
||||
|
||||
if strconv.Itoa(res.StatusCode)[0:1] == "5" {
|
||||
api.Log.Error("API gave internal server error", "statusCode", res.StatusCode)
|
||||
return ApiRes{}, errors.New("API gave internal server error")
|
||||
} else if res.StatusCode == 403 {
|
||||
api.Log.Error("API responded with 403, Forbidden")
|
||||
return ApiRes{}, errors.New("API responded with 403, Forbidden")
|
||||
} else if res.StatusCode == 401 {
|
||||
api.Log.Error("API responded with 401, Unauthorized")
|
||||
return ApiRes{}, errors.New("API responded with 403, Unauthorized")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not read res body", "err", err.Error())
|
||||
return ApiRes{}, err
|
||||
}
|
||||
|
||||
respObj.Body = body
|
||||
|
||||
return respObj, nil
|
||||
}
|
||||
|
||||
func (api Api) getAdminToken() (string, AdminClaims, error) {
|
||||
if api.AdminToken == "" {
|
||||
// No adminToken exists, obtain it
|
||||
|
||||
adminToken, err := api.getAdminTokenCall()
|
||||
api.AdminToken = adminToken
|
||||
|
||||
if err != nil {
|
||||
api.Log.Error("Could not get admin token")
|
||||
return "", AdminClaims{}, err
|
||||
}
|
||||
|
||||
parsedToken, err := api.parseAdminToken(api.AdminToken)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not parse admin token")
|
||||
return "", AdminClaims{}, err
|
||||
}
|
||||
|
||||
return api.AdminToken, parsedToken, err
|
||||
}
|
||||
|
||||
parsedToken, err := api.parseAdminToken(api.AdminToken)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not parse admin token")
|
||||
return "", AdminClaims{}, err
|
||||
}
|
||||
|
||||
return api.AdminToken, parsedToken, nil
|
||||
}
|
||||
|
||||
func (api Api) getAdminTokenCall() (string, error) {
|
||||
req, err := http.NewRequest("POST", api.URL+"/auth/api-key", bytes.NewBufferString("\""+api.AdminApiKey+"\""))
|
||||
if err != nil {
|
||||
api.Log.Error("Could not create request", "err", err.Error())
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpClient := &http.Client{}
|
||||
res, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not call backend API", "err", err.Error())
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
api.Log.Debug("API res status", "status", res.StatusCode)
|
||||
|
||||
if strconv.Itoa(res.StatusCode)[0:1] == "5" {
|
||||
api.Log.Error("API gave internal server error", "statusCode", res.StatusCode)
|
||||
return "", errors.New("API gave internal server error")
|
||||
} else if res.StatusCode != 200 {
|
||||
api.Log.Error("API gave unexpected statusCode", "statusCode", res.StatusCode)
|
||||
return "", errors.New("API gave unexpected statusCode")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not read res body", "err", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
var jsonResp TokenRes
|
||||
|
||||
err = json.Unmarshal(body, &jsonResp)
|
||||
if err != nil {
|
||||
api.Log.Error("Could not parse JSON from API", "err", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
return jsonResp.Jwt, nil
|
||||
}
|
||||
|
||||
func (api Api) parseAdminToken(token string) (AdminClaims, error) {
|
||||
claims := &AdminClaims{}
|
||||
parsedToken, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(api.JwtSharedSecret), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
api.Log.Error("Could not parse admin token", "err", err.Error())
|
||||
return AdminClaims{}, err
|
||||
}
|
||||
|
||||
if !parsedToken.Valid {
|
||||
err := errors.New("invalid token")
|
||||
api.Log.Error("Invalid token")
|
||||
return AdminClaims{}, err
|
||||
}
|
||||
|
||||
claims, ok := parsedToken.Claims.(*AdminClaims)
|
||||
if !ok {
|
||||
err := errors.New("parsedToken.Claims.(*AdminClaims) did not return ok")
|
||||
api.Log.Error(err.Error())
|
||||
return AdminClaims{}, err
|
||||
}
|
||||
|
||||
return *claims, nil
|
||||
}
|
36
src/api/types.go
Normal file
36
src/api/types.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
AdminApiKey string
|
||||
AdminToken string
|
||||
JwtSharedSecret string
|
||||
Log *zap.SugaredLogger
|
||||
URL string
|
||||
}
|
||||
|
||||
type ApiRes struct {
|
||||
Body []byte
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
type ApiResError struct {
|
||||
Error string `json:"error"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
type TokenRes struct {
|
||||
Jwt string `json:"jwt"`
|
||||
RenewalToken string `json:"renewalToken"`
|
||||
}
|
||||
|
||||
type AdminClaims struct {
|
||||
AccountID string `json:"accountId"`
|
||||
AccountFields map[string][]string `json:"accountFields"`
|
||||
AccountName string `json:"accountName"`
|
||||
jwt.StandardClaims
|
||||
}
|
|
@ -1,26 +1,25 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/api"
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/views"
|
||||
)
|
||||
|
||||
func (h Handlers) Register(c *fiber.Ctx) error {
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||
return c.SendString(views.Register())
|
||||
return c.SendString(views.Register(views.RegisterData{}))
|
||||
}
|
||||
|
||||
type NewUser struct {
|
||||
Username string
|
||||
Password string
|
||||
RepeatPassword string
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
RepeatPassword string `json:"-"`
|
||||
}
|
||||
|
||||
func (h Handlers) RegisterPost(c *fiber.Ctx) error {
|
||||
|
@ -32,8 +31,7 @@ func (h Handlers) RegisterPost(c *fiber.Ctx) error {
|
|||
h.Log.Debug("Invalid input data", "err", err.Error())
|
||||
c.Status(400)
|
||||
}
|
||||
|
||||
fmt.Printf("newUser: %v", newUser)
|
||||
newUser.Name = newUser.Username
|
||||
|
||||
jsonPayload, err := json.Marshal(newUser)
|
||||
if err != nil {
|
||||
|
@ -41,30 +39,33 @@ func (h Handlers) RegisterPost(c *fiber.Ctx) error {
|
|||
return c.Status(500).SendString(views.Error500())
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:4000/account", bytes.NewBuffer(jsonPayload))
|
||||
if err != nil {
|
||||
h.Log.Error("Could not create POST request", "err", err.Error())
|
||||
return c.Status(500).SendString(views.Error500())
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpClient := &http.Client{}
|
||||
res, err := httpClient.Do(req)
|
||||
apiRes, err := h.Api.Call("POST", "/account", jsonPayload)
|
||||
if err != nil {
|
||||
h.Log.Error("Could not call backend API", "err", err.Error())
|
||||
return c.Status(500).SendString(views.Error500())
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
h.Log.Debug("API res status", "status", res.Status)
|
||||
h.Log.Debug("API res status", "status", apiRes.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if apiRes.StatusCode == 201 {
|
||||
return c.SendString(views.Register(views.RegisterData{
|
||||
OkMsg: "New user created!",
|
||||
}))
|
||||
} else if strconv.Itoa(apiRes.StatusCode)[0:1] == "4" {
|
||||
var jsonResp []api.ApiResError
|
||||
|
||||
err := json.Unmarshal(apiRes.Body, &jsonResp)
|
||||
if err != nil {
|
||||
h.Log.Error("Could not read res body", "err", err.Error())
|
||||
h.Log.Error("Could not unmarshal api error response", "err", err.Error())
|
||||
return c.Status(500).SendString(views.Error500())
|
||||
}
|
||||
|
||||
fmt.Println("response Body:", string(body))
|
||||
|
||||
return c.SendString(views.Register())
|
||||
return c.Status(apiRes.StatusCode).SendString(views.Register(views.RegisterData{
|
||||
ErrField: jsonResp[0].Field,
|
||||
ErrMsg: jsonResp[0].Error,
|
||||
}))
|
||||
} else {
|
||||
h.Log.Error("Unexpected API response status", "status", apiRes.StatusCode)
|
||||
return c.Status(500).SendString(views.Error500())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package handlers
|
|||
|
||||
import (
|
||||
// jwt "github.com/dgrijalva/jwt-go"
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/api"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Handlers is the overall struct for all http request handlers
|
||||
type Handlers struct {
|
||||
JwtKey []byte
|
||||
Api api.Api
|
||||
Log *zap.SugaredLogger
|
||||
}
|
||||
|
|
17
src/main.go
17
src/main.go
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/joho/godotenv"
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/api"
|
||||
h "gitlab.larvit.se/power-plan/auth-ui/src/handlers"
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/utils"
|
||||
)
|
||||
|
@ -24,11 +25,23 @@ func main() {
|
|||
if os.Getenv("ADMIN_API_KEY") == "changeMe" {
|
||||
log.Error("ADMIN_API_KEY ENV is not set, using very insecure \"changeMe\"")
|
||||
}
|
||||
jwtKey := []byte(os.Getenv("JWT_SHARED_SECRET"))
|
||||
jwtSharedSecret := os.Getenv("JWT_SHARED_SECRET")
|
||||
adminApiKey := os.Getenv("ADMIN_API_KEY")
|
||||
apiUrl := os.Getenv("API_URL")
|
||||
|
||||
app := fiber.New()
|
||||
|
||||
handlers := h.Handlers{JwtKey: jwtKey, Log: log}
|
||||
apiInstance := api.Api{
|
||||
AdminApiKey: adminApiKey,
|
||||
JwtSharedSecret: jwtSharedSecret,
|
||||
Log: log,
|
||||
URL: apiUrl,
|
||||
}
|
||||
|
||||
handlers := h.Handlers{
|
||||
Api: apiInstance,
|
||||
Log: log,
|
||||
}
|
||||
|
||||
// Log all requests
|
||||
app.Use(handlers.LogReq)
|
||||
|
|
12
src/view-utils/vutils.go
Normal file
12
src/view-utils/vutils.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package vu // View Utils
|
||||
|
||||
// Replacement for ternary. Use like:
|
||||
// str := "Foo " + Tern(len("") != 0, "bar", "baz") + " yes" // Evaluates to: "Foo baz yes"
|
||||
// str := "Foo " + Tern(true, "bar", "baz") + " yes" // Evaluates to: "Foo bar yes"
|
||||
func TernStr(exp bool, expTrue string, expFalse string) string {
|
||||
if exp {
|
||||
return expTrue
|
||||
} else {
|
||||
return expFalse
|
||||
}
|
||||
}
|
25
src/view-utils/vutils_test.go
Normal file
25
src/view-utils/vutils_test.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package vu
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTern(t *testing.T) {
|
||||
test1 := "a" + TernStr(1 == 2, "b", "c") + "d"
|
||||
if test1 != "acd" {
|
||||
t.Fatalf("Expected \"acd\" but got %v", test1)
|
||||
}
|
||||
|
||||
test2 := "a" + TernStr(1 == 1, "b", "c") + "d"
|
||||
if test2 != "abd" {
|
||||
t.Fatalf("Expected \"abd\" but got %v", test1)
|
||||
}
|
||||
|
||||
test3 := "a" + TernStr(false, "b", "c") + "d"
|
||||
if test3 != "acd" {
|
||||
t.Fatalf("Expected \"acd\" but got %v", test1)
|
||||
}
|
||||
|
||||
test4 := "a" + TernStr(true, "b", "c") + "d"
|
||||
if test4 != "abd" {
|
||||
t.Fatalf("Expected \"abd\" but got %v", test1)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,21 @@
|
|||
package views
|
||||
|
||||
import "gitlab.larvit.se/power-plan/auth-ui/src/views/layouts"
|
||||
import (
|
||||
vu "gitlab.larvit.se/power-plan/auth-ui/src/view-utils"
|
||||
"gitlab.larvit.se/power-plan/auth-ui/src/views/layouts"
|
||||
)
|
||||
|
||||
func Register() string {
|
||||
type RegisterData struct {
|
||||
ErrField string
|
||||
ErrMsg string
|
||||
OkMsg string
|
||||
}
|
||||
|
||||
func Register(data RegisterData) string {
|
||||
content := `
|
||||
<h1 class="main-title">Register</h1>
|
||||
<form method="post" class="pure-form pure-form-aligned primary-form">
|
||||
` + vu.TernStr(len(data.OkMsg) != 0, "<p>"+data.OkMsg+" <a href=\"/\">Login</a></p>", "") + `
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
<label for="username">Username</label>
|
||||
|
@ -21,7 +31,7 @@ func Register() string {
|
|||
<label for="repeatPassword">Repeat password</label>
|
||||
<input type="password" placeholder="Repeat password" name="repeatPassword" id="repeatPassword" />
|
||||
</div>
|
||||
|
||||
` + vu.TernStr(len(data.ErrMsg) != 0, "<p>ERROR! "+data.ErrMsg+"<p>", "") + `
|
||||
<div class="pure-controls">
|
||||
<a href="/" class="pure-button">Cancel</a>
|
||||
<button type="submit" class="pure-button pure-button-primary">Register</button>
|
||||
|
|
Loading…
Reference in New Issue
Block a user