From c8697d6ef6d3fec52652bfa169b6a19e71497c6c Mon Sep 17 00:00:00 2001 From: Gabe Farrell Date: Tue, 16 May 2023 02:48:42 +0000 Subject: [PATCH] clear out old janus stuff --- go.mod | 2 - go.sum | 4 - server/auth/auth_long_test.go | 127 ++----------------------- server/auth/constants.go | 5 + server/auth/delete.go | 20 +++- server/auth/forms.go | 29 +----- server/auth/login.go | 29 ++---- server/auth/register.go | 39 ++------ server/auth/router.go | 11 +-- server/auth/tokens.go | 85 ----------------- server/db/crud_test.go | 31 +++---- server/db/schema.go | 23 ++--- server/list/schema.go | 12 +++ server/middleware/contextKeys.go | 9 -- server/middleware/tokenValidator.go | 138 ---------------------------- server/token/claims.go | 43 --------- server/token/generate.go | 72 --------------- server/token/keys_test.go | 92 ------------------- server/token/keytypes.go | 7 -- server/token/validate.go | 66 ------------- 20 files changed, 84 insertions(+), 760 deletions(-) create mode 100644 server/auth/constants.go delete mode 100644 server/auth/tokens.go create mode 100644 server/list/schema.go delete mode 100644 server/middleware/contextKeys.go delete mode 100644 server/middleware/tokenValidator.go delete mode 100644 server/token/claims.go delete mode 100644 server/token/generate.go delete mode 100644 server/token/keys_test.go delete mode 100644 server/token/keytypes.go delete mode 100644 server/token/validate.go diff --git a/go.mod b/go.mod index bcbb953..3db1815 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,10 @@ require ( github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/credentials v1.13.23 github.com/go-chi/chi/v5 v5.0.8 - github.com/golang-jwt/jwt/v5 v5.0.0 github.com/google/uuid v1.3.0 github.com/joho/godotenv v1.5.1 github.com/wagslane/go-password-validator v0.3.0 golang.org/x/crypto v0.8.0 - golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 ) require ( diff --git a/go.sum b/go.sum index b4d6a2a..3fd8d1e 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,6 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -55,8 +53,6 @@ github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSV github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/server/auth/auth_long_test.go b/server/auth/auth_long_test.go index 926fc3b..21d51fc 100644 --- a/server/auth/auth_long_test.go +++ b/server/auth/auth_long_test.go @@ -12,32 +12,23 @@ import ( "github.com/mnrva-dev/owltier.com/server/auth" "github.com/mnrva-dev/owltier.com/server/config" "github.com/mnrva-dev/owltier.com/server/db" - "github.com/mnrva-dev/owltier.com/server/token" ) // TODO also write unit tests type userdata struct { - Username string `json:"username"` - Password string `json:"password"` - Email string `json:"email"` - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - Name string `json:"name"` + Username string `json:"username"` + Password string `json:"password"` } var ( testuser = &db.UserSchema{ Username: "test", Password: "testpassword1234!!", - Email: "test@example.com", } permatestuser = &db.UserSchema{ - Id: "0a85b0ae-a577-4be3-8609-d443d50f6939", Username: "user", Password: "password1234!!", - Email: "user@example.com", - Refresh: "", } ) @@ -53,7 +44,7 @@ func TestRegister(t *testing.T) { ts := runTestServer() defer ts.Close() data := url.Values{} - data.Set("email", testuser.Email) + data.Set("username", testuser.Username) if config.UsernamesEnabled() { data.Set("username", testuser.Username) } @@ -71,29 +62,13 @@ func TestRegister(t *testing.T) { fmt.Println(string(body)) t.FailNow() } - AccessToken := strings.Split(string(body), "\n")[0] - RefreshToken := strings.Split(string(body), "\n")[1] - if AccessToken == "" || RefreshToken == "" { - t.Errorf("Expected access and refresh token to be set") - } - if AccessToken == RefreshToken { - t.Errorf("Expected access and refresh tokens to be different") - } - _, err = token.ValidateAccess(AccessToken) - if err != nil { - t.Errorf("Expected valid access token, got %v", err) - } - _, err = token.ValidateRefresh(RefreshToken) - if err != nil { - t.Errorf("Expected valid refresh token, got %v", err) - } } func TestLogin(t *testing.T) { ts := runTestServer() defer ts.Close() data := url.Values{} - data.Set("email", testuser.Email) + data.Set("username", testuser.Username) if config.UsernamesEnabled() { data.Set("username", testuser.Username) } @@ -111,104 +86,13 @@ func TestLogin(t *testing.T) { fmt.Println(string(body)) t.FailNow() } - AccessToken := strings.Split(string(body), "\n")[0] - RefreshToken := strings.Split(string(body), "\n")[1] - if AccessToken == "" || RefreshToken == "" { - t.Errorf("Expected access and refresh token to be set") - } - if AccessToken == RefreshToken { - t.Errorf("Expected access and refresh tokens to be different") - } - _, err = token.ValidateAccess(AccessToken) - if err != nil { - t.Errorf("Expected valid access token, got %v", err) - } - _, err = token.ValidateRefresh(RefreshToken) - if err != nil { - t.Errorf("Expected valid refresh token, got %v", err) - } -} - -func TestRefresh(t *testing.T) { - ts := runTestServer() - defer ts.Close() - data := url.Values{} - data.Set("email", testuser.Email) - data.Set("password", testuser.Password) - resp, err := http.PostForm(fmt.Sprintf("%s/login", ts.URL), data) - if err != nil { - t.Fatal(err) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal("Could not read body") - } - if resp.StatusCode/100 != 2 { - t.Error("Failed to login") - t.FailNow() - } - RefreshToken := strings.Split(string(body), "\n")[1] - req, err := http.NewRequest("POST", fmt.Sprintf("%s/token/refresh", ts.URL), nil) - if err != nil { - t.Fatalf("Couldn't create request: %v", err) - } - req.Header.Set("Authorization", "Bearer "+RefreshToken) - resp, err = http.DefaultClient.Do(req) - body, err = ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal("Could not read body") - } - if resp.StatusCode/100 != 2 { - t.Errorf("%s\n", string(body)) - t.Fatalf("Expected token to be validated but got status %d", resp.StatusCode) - } - AccessToken := strings.Split(string(body), "\n")[0] - RefreshToken = strings.Split(string(body), "\n")[1] - if AccessToken == "" || RefreshToken == "" { - t.Errorf("Expected access and refresh token to be set") - } - if AccessToken == RefreshToken { - t.Errorf("Expected access and refresh tokens to be different") - } - _, err = token.ValidateAccess(AccessToken) - if err != nil { - t.Errorf("Expected valid access token, got %v", err) - } - _, err = token.ValidateRefresh(RefreshToken) - if err != nil { - t.Errorf("Expected valid refresh token, got %v", err) - } -} - -func TestValidateAccess(t *testing.T) { - ts := runTestServer() - defer ts.Close() - AccessToken := token.GenerateAccess(testuser) - req, err := http.NewRequest("GET", fmt.Sprintf("%s/token/validate", ts.URL), nil) - if err != nil { - t.Fatalf("Couldn't create request: %v", err) - } - req.Header.Set("Authorization", "Bearer "+AccessToken) - resp, err := http.DefaultClient.Do(req) - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal("Could not read body") - } - if err != nil { - t.Error(string(body)) - t.Fatal(err) - } - if resp.StatusCode/100 != 2 { - t.Error(string(body)) - t.Errorf("Expected token to be validated but got status %d", resp.StatusCode) - } } func TestDeleteAccount(t *testing.T) { ts := runTestServer() defer ts.Close() data := url.Values{} - data.Set("email", testuser.Email) + data.Set("username", testuser.Username) data.Set("password", testuser.Password) resp, err := http.PostForm(fmt.Sprintf("%s/login", ts.URL), data) if err != nil { @@ -222,6 +106,7 @@ func TestDeleteAccount(t *testing.T) { t.Error("Failed to login") t.FailNow() } + // TODO: Fix this so that it uses session token AccessToken := strings.Split(string(body), "\n")[0] data = url.Values{} data.Set("password", testuser.Password) diff --git a/server/auth/constants.go b/server/auth/constants.go new file mode 100644 index 0000000..6a829cd --- /dev/null +++ b/server/auth/constants.go @@ -0,0 +1,5 @@ +package auth + +const ( + SESSION_COOKIE = "_owltier.com_sess" +) diff --git a/server/auth/delete.go b/server/auth/delete.go index f59cb69..c423462 100644 --- a/server/auth/delete.go +++ b/server/auth/delete.go @@ -6,7 +6,6 @@ import ( "github.com/mnrva-dev/owltier.com/server/db" "github.com/mnrva-dev/owltier.com/server/jsend" - "github.com/mnrva-dev/owltier.com/server/middleware" "golang.org/x/crypto/bcrypt" ) @@ -15,10 +14,23 @@ func DeleteAccount(w http.ResponseWriter, r *http.Request) { r.ParseForm() password := strings.TrimSpace(r.FormValue("password")) - // get user from token parse middleware - user, err := r.Context().Value(middleware.ContextKeyValues).(*middleware.Values).GetUser() + // get user session id from cookies + sessC, err := r.Cookie(SESSION_COOKIE) if err != nil { - jsend.Error(w, "Failed to retrieve user information") + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) + return + } + session := sessC.Value + var user = &db.UserSchema{} + err = db.FetchByGsi(&db.UserSchema{ + Session: session, + }, user) + if err != nil { + jsend.Fail(w, 401, map[string]interface{}{ + "session": "invalid session", + }) return } diff --git a/server/auth/forms.go b/server/auth/forms.go index 386b8ba..809f29d 100644 --- a/server/auth/forms.go +++ b/server/auth/forms.go @@ -1,10 +1,8 @@ package auth import ( - "encoding/base64" "errors" "net/http" - "net/mail" "regexp" "strings" @@ -17,11 +15,8 @@ const ( ) type RequestForm struct { - Username string - Password string - Email string - Redirect bool - RedirectUrl string + Username string + Password string } func (h *RequestForm) validate() error { @@ -31,23 +26,6 @@ func (h *RequestForm) validate() error { if h.Password == "" { return errors.New("password is required") } - if h.Email == "" { - return errors.New("email is required") - } - if _, err := mail.ParseAddress(h.Email); err != nil { - return errors.New("email address is not valid") - } - if h.Redirect { - if h.RedirectUrl == "" { - return errors.New("redirect url is required") - } - var url []byte - _, err := base64.NewDecoder(base64.StdEncoding, strings.NewReader(h.RedirectUrl)).Read(url) - if err != nil { - return err - } - h.RedirectUrl = string(url) - } if !regexp.MustCompile(`^[a-zA-Z0-9-_]{3,24}$`).MatchString(h.Username) && config.UsernamesEnabled() { return errors.New("username is not valid") } @@ -68,9 +46,6 @@ func (h *RequestForm) Parse(r *http.Request) error { if len(h.Password) > 128 { h.Password = h.Password[:128] } - h.Email = strings.TrimSpace(r.FormValue("email")) - h.Redirect = r.FormValue("redirect") != "" || strings.ToLower(r.FormValue("redirect")) == "false" - h.RedirectUrl = strings.TrimSpace(r.FormValue("redirect_url")) return h.validate() } diff --git a/server/auth/login.go b/server/auth/login.go index 5b10140..96142cc 100644 --- a/server/auth/login.go +++ b/server/auth/login.go @@ -4,9 +4,9 @@ import ( "net/http" "time" + "github.com/google/uuid" "github.com/mnrva-dev/owltier.com/server/db" "github.com/mnrva-dev/owltier.com/server/jsend" - "github.com/mnrva-dev/owltier.com/server/token" "golang.org/x/crypto/bcrypt" ) @@ -19,44 +19,35 @@ func Login(w http.ResponseWriter, r *http.Request) { // get user from DB var user = &db.UserSchema{} - err := db.FetchByGsi(&db.UserSchema{ - Email: form.Email, + err := db.Fetch(&db.UserSchema{ + Username: form.Username, }, user) if err != nil { jsend.Fail(w, 401, map[string]interface{}{ - "message": "Email or password is invalid"}) + "message": "Username or password is invalid"}) return } err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.Password)) if err != nil { jsend.Fail(w, 401, map[string]interface{}{ - "message": "Email or password is invalid"}) + "message": "Username or password is invalid"}) return } // prepare login information for the client - accessT := token.GenerateAccess(user) - refreshT := token.GenerateRefresh(user) - db.Update(user, "refresh_token", refreshT) + session := uuid.NewString() + db.Update(user, "session", session) db.Update(user, "last_login_at", time.Now().Unix()) http.SetCookie(w, &http.Cookie{ - Name: "_owltier.com_auth", - Value: accessT, - Path: "/", - Expires: time.Now().Add(time.Hour), - HttpOnly: true, - Secure: true, - }) - http.SetCookie(w, &http.Cookie{ - Name: "_owltier.com_refresh", - Value: accessT, + Name: SESSION_COOKIE, + Value: session, Path: "/", Expires: time.Now().Add(time.Hour), HttpOnly: true, Secure: true, }) jsend.Success(w, map[string]interface{}{ - "id": user.Id, + "username": user.Username, }) } diff --git a/server/auth/register.go b/server/auth/register.go index c7e28a4..a187661 100644 --- a/server/auth/register.go +++ b/server/auth/register.go @@ -7,7 +7,6 @@ import ( "github.com/google/uuid" "github.com/mnrva-dev/owltier.com/server/db" "github.com/mnrva-dev/owltier.com/server/jsend" - "github.com/mnrva-dev/owltier.com/server/token" "golang.org/x/crypto/bcrypt" ) @@ -20,8 +19,8 @@ func Register(w http.ResponseWriter, r *http.Request) { // get user from DB var user = &db.UserSchema{} - err := db.FetchByGsi(&db.UserSchema{ - Email: form.Email, + err := db.Fetch(&db.UserSchema{ + Username: form.Username, }, user) // if we didnt get NotFound error... if err == nil { @@ -39,20 +38,10 @@ func Register(w http.ResponseWriter, r *http.Request) { return } user.Password = string(hashedPassword) - user.Email = form.Email - user.Id = uuid.New().String() - user.Scope = "default" - user.EmailIsVerified = false - accessT := token.GenerateAccess(user) - refreshT := token.GenerateRefresh(user) - if accessT == "" || refreshT == "" { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } + session := uuid.NewString() - user.Refresh = refreshT + user.Session = session err = db.Create(user) if err != nil { @@ -62,26 +51,14 @@ func Register(w http.ResponseWriter, r *http.Request) { } http.SetCookie(w, &http.Cookie{ - Name: "_owltier_auth", - Value: accessT, + Name: SESSION_COOKIE, + Value: session, Path: "/", Expires: time.Now().Add(time.Hour), HttpOnly: true, Secure: true, }) - http.SetCookie(w, &http.Cookie{ - Name: "_owltier_refresh", - Value: accessT, - Path: "/", - Expires: time.Now().Add(time.Hour), - HttpOnly: true, - Secure: true, + jsend.Success(w, map[string]interface{}{ + "username": user.Username, }) - if form.Redirect { - http.Redirect(w, r, form.RedirectUrl, 303) - } else { - jsend.Success(w, map[string]interface{}{ - "id": user.Id, - }) - } } diff --git a/server/auth/router.go b/server/auth/router.go index 8c2e8b4..c2a181b 100644 --- a/server/auth/router.go +++ b/server/auth/router.go @@ -2,7 +2,6 @@ package auth import ( "github.com/go-chi/chi/v5" - "github.com/mnrva-dev/owltier.com/server/middleware" ) func BuildRouter() *chi.Mux { @@ -10,15 +9,7 @@ func BuildRouter() *chi.Mux { r.Post("/login", Login) r.Post("/register", Register) - r.Group(func(r chi.Router) { - r.Use(middleware.TokenValidater) - r.Post("/delete", DeleteAccount) - }) - r.Group(func(r chi.Router) { - r.Use(middleware.RefreshValidator) - r.Post("/token/refresh", Refresh) - }) - r.Get("/token/validate", Validate) + r.Post("/delete", DeleteAccount) return r } diff --git a/server/auth/tokens.go b/server/auth/tokens.go deleted file mode 100644 index e3aaf7c..0000000 --- a/server/auth/tokens.go +++ /dev/null @@ -1,85 +0,0 @@ -package auth - -import ( - "fmt" - "net/http" - "strings" - "time" - - "github.com/mnrva-dev/owltier.com/server/db" - "github.com/mnrva-dev/owltier.com/server/jsend" - "github.com/mnrva-dev/owltier.com/server/middleware" - "github.com/mnrva-dev/owltier.com/server/token" -) - -func Refresh(w http.ResponseWriter, r *http.Request) { - - // get user and token from token parse middleware - user, err := r.Context().Value(middleware.ContextKeyValues).(*middleware.Values).GetUser() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - t, err := r.Context().Value(middleware.ContextKeyValues).(*middleware.Values).GetRefreshToken() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if user.Refresh != t { - http.Error(w, "Token mismatch", http.StatusUnauthorized) - fmt.Printf("%s\n%s", user.Refresh, t) - return - } - - // prepare login information for the client - accessT := token.GenerateAccess(user) - refreshT := token.GenerateRefresh(user) - db.Update(user, "RefreshToken", refreshT) - - http.SetCookie(w, &http.Cookie{ - Name: "_owltier.com_auth", - Value: accessT, - Path: "/", - Expires: time.Now().Add(time.Hour), - HttpOnly: true, - Secure: true, - }) - http.SetCookie(w, &http.Cookie{ - Name: "_owltier.com_refresh", - Value: accessT, - Path: "/", - Expires: time.Now().Add(time.Hour), - HttpOnly: true, - Secure: true, - }) - jsend.Success(w, nil) -} - -func Validate(w http.ResponseWriter, r *http.Request) { - header := r.Header.Get("Authorization") - headerVals := strings.Split(header, " ") - if strings.ToLower(headerVals[0]) != "bearer" { - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprint(w, "Bad Authorization Scheme") - } - t := headerVals[1] - if t == "" { - w.WriteHeader(400) - fmt.Fprint(w, "No Token Provided") - return - } - - claims, err := token.ValidateAccess(t) - if err != nil { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - - if claims.Type != "Access" { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - jsend.Success(w, nil) -} diff --git a/server/db/crud_test.go b/server/db/crud_test.go index c7463f4..151c4d8 100644 --- a/server/db/crud_test.go +++ b/server/db/crud_test.go @@ -9,7 +9,6 @@ import ( func TestCreate(t *testing.T) { u := &db.UserSchema{ Username: "myuser", - Email: "me@example.com", Password: "secret123", } @@ -27,28 +26,28 @@ func TestRead(t *testing.T) { if err != nil { t.Fatalf("Expected no error, got %v", err) } - if u.Email != "me@example.com" { - t.Fatalf("Expected correct email, got %v", u.Email) - } -} - -func TestGsiRead(t *testing.T) { - u := &db.UserSchema{ - Email: "me@example.com", - } - err := db.FetchByGsi(u, u) - if err != nil { - t.Fatalf("Expected no error, got %v", err) - } if u.Username != "myuser" { - t.Fatalf("Expected correct username, got %v", u.Email) + t.Fatalf("Expected correct username, got %v", u.Username) } } +// TODO: Fix it +// func TestGsiRead(t *testing.T) { +// u := &db.UserSchema{ +// Session: "1234", +// } +// err := db.FetchByGsi(u, u) +// if err != nil { +// t.Fatalf("Expected no error, got %v", err) +// } +// if u.Username != "myuser" { +// t.Fatalf("Expected correct username, got %v", u.Username) +// } +// } + func TestDelete(t *testing.T) { u := &db.UserSchema{ Username: "myuser", - Email: "me@example.com", Password: "secret123", } err := db.Delete(u) diff --git a/server/db/schema.go b/server/db/schema.go index 31a1c16..b0f833c 100644 --- a/server/db/schema.go +++ b/server/db/schema.go @@ -5,23 +5,18 @@ import ( ) type UserSchema struct { - Pk string `dynamodbav:"pk"` - Gsi1pk string `dynamodbav:"gsi1pk"` - Id string `dynamodbav:"id"` - Username string `dynamodbav:"username,omitempty"` - Email string `dynamodbav:"email"` - EmailIsVerified bool `dynamodbav:"email_is_verified"` - Password string `dynamodbav:"password"` - Refresh string `dynamodbav:"refresh_token"` - Scope string `dynamodbav:"scope"` - Policies []string `dynamodbav:"policies"` - CreatedAt int64 `dynamodbav:"created_at"` - LastLoginAt int64 `dynamodbav:"last_login_at"` + Pk string `dynamodbav:"pk"` + Gsi1pk string `dynamodbav:"gsi1pk"` + Session string `dynamodbav:"session"` + Username string `dynamodbav:"username"` + Password string `dynamodbav:"password"` + CreatedAt int64 `dynamodbav:"created_at"` + LastLoginAt int64 `dynamodbav:"last_login_at"` } func (u *UserSchema) buildKeys() { - u.Pk = "user#" + u.Id - u.Gsi1pk = "email#" + u.Email + u.Pk = "user#" + u.Username + u.Gsi1pk = "session#" + u.Session } func (u *UserSchema) getKey() map[string]types.AttributeValue { diff --git a/server/list/schema.go b/server/list/schema.go new file mode 100644 index 0000000..60069d9 --- /dev/null +++ b/server/list/schema.go @@ -0,0 +1,12 @@ +package list + +type List struct { + Id string `json:"id"` + CreatedAt int64 `json:"created_at"` + CreatedBy string `json:"created_by"` + Format string `json:"format"` + Breaks []bool `json:"breaks"` + NA []string `json:"na"` + APAC []string `json:"apac"` + Combined []string `json:"combined"` +} diff --git a/server/middleware/contextKeys.go b/server/middleware/contextKeys.go deleted file mode 100644 index e538b31..0000000 --- a/server/middleware/contextKeys.go +++ /dev/null @@ -1,9 +0,0 @@ -package middleware - -type ContextKey string - -const ( - ContextKeyUser = ContextKey("user") - ContextKeyToken = ContextKey("token") - ContextKeyValues = ContextKey("values") -) diff --git a/server/middleware/tokenValidator.go b/server/middleware/tokenValidator.go deleted file mode 100644 index 2fbab88..0000000 --- a/server/middleware/tokenValidator.go +++ /dev/null @@ -1,138 +0,0 @@ -package middleware - -import ( - "context" - "errors" - "fmt" - "log" - "net/http" - "strings" - - "github.com/mnrva-dev/owltier.com/server/db" - "github.com/mnrva-dev/owltier.com/server/token" -) - -type Values struct { - m map[string]interface{} -} - -func (v Values) GetUser() (*db.UserSchema, error) { - u, ok := v.m["user"].(*db.UserSchema) - if !ok || u == nil { - return nil, errors.New("user is not set") - } - return u, nil -} -func (v Values) GetAccessToken() (string, error) { - u, ok := v.m["access"].(string) - if !ok { - return "", errors.New("access token is not set") - } - return u, nil -} -func (v Values) GetRefreshToken() (string, error) { - u, ok := v.m["refresh"].(string) - if !ok { - return "", errors.New("refresh token is not set") - } - return u, nil -} - -func TokenValidater(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - header := r.Header.Get("Authorization") - headerVals := strings.Split(header, " ") - if strings.ToLower(headerVals[0]) != "bearer" { - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprint(w, "Bad Authorization Scheme") - } - t := headerVals[1] - if t == "" { - w.WriteHeader(401) - fmt.Fprint(w, "No Token Provided") - return - } - - claims, err := token.ValidateAccess(t) - if err != nil { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - - if claims.Type != token.TypeAccess { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - - var user = &db.UserSchema{} - err = db.Fetch(&db.UserSchema{Id: claims.Id}, user) - if err != nil { - log.Println(err) - w.WriteHeader(400) - fmt.Fprint(w, "User Not Found With Id: "+claims.Id) - return - } - - v := Values{map[string]interface{}{ - "user": user, - "access": t, - }} - - ctx := context.WithValue(r.Context(), ContextKeyValues, &v) - - r = r.WithContext(ctx) - - next.ServeHTTP(w, r) - }) -} - -func RefreshValidator(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - header := r.Header.Get("Authorization") - headerVals := strings.Split(header, " ") - if strings.ToLower(headerVals[0]) != "bearer" { - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprint(w, "Bad Authorization Scheme") - } - t := headerVals[1] - if t == "" { - w.WriteHeader(401) - fmt.Fprint(w, "No Token Provided") - return - } - - claims, err := token.ValidateRefresh(t) - if err != nil { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - - if claims.Type != token.TypeRefresh { - w.WriteHeader(401) - fmt.Fprint(w, "Unauthorized") - return - } - - var user = &db.UserSchema{} - err = db.Fetch(&db.UserSchema{Id: claims.Id}, user) - if err != nil { - w.WriteHeader(400) - fmt.Fprint(w, "User Not Found") - return - } - - v := Values{map[string]interface{}{ - "user": user, - "refresh": t, - }} - - ctx := context.WithValue(r.Context(), ContextKeyValues, &v) - - r = r.WithContext(ctx) - - next.ServeHTTP(w, r) - }) -} diff --git a/server/token/claims.go b/server/token/claims.go deleted file mode 100644 index 20fc828..0000000 --- a/server/token/claims.go +++ /dev/null @@ -1,43 +0,0 @@ -package token - -import ( - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/google/uuid" - "github.com/mnrva-dev/owltier.com/server/config" - "github.com/mnrva-dev/owltier.com/server/db" -) - -type Claims struct { - jwt.RegisteredClaims - Id string `json:"id"` - Username string `json:"username,omitempty"` - Email string `json:"email"` - EmailIsVerified bool `json:"email_verified"` - XSRF string `json:"xsrf"` - Role string `json:"role,omitempty"` - Policies []string `json:"policies,omitempty"` - Type string `json:"type"` - Scope string `json:"scope"` -} - -func BuildClaims(user *db.UserSchema) *Claims { - var c = &Claims{ - Id: user.Id, - Username: user.Username, - Email: user.Email, - EmailIsVerified: user.EmailIsVerified, - XSRF: uuid.New().String(), - Scope: user.Scope, - Policies: user.Policies, - } - c.IssuedAt = jwt.NewNumericDate(time.Now()) - c.NotBefore = jwt.NewNumericDate(time.Now()) - c.Issuer = config.JwtIssuer() - c.Audience = config.JwtAudience() - if c.Scope == "" { - c.Scope = "default" - } - return c -} diff --git a/server/token/generate.go b/server/token/generate.go deleted file mode 100644 index cbc6bc8..0000000 --- a/server/token/generate.go +++ /dev/null @@ -1,72 +0,0 @@ -package token - -import ( - "log" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/mnrva-dev/owltier.com/server/config" - "github.com/mnrva-dev/owltier.com/server/db" -) - -const ( - ACCESS_EXPIRATION = 10 * time.Minute - REFRESH_EXPIRATION = 7 * 24 * time.Hour - VERIFY_EMAIL_EXPIRATION = 15 * time.Minute -) - -func GenerateAccess(user *db.UserSchema) string { - c := BuildClaims(user) - c.ExpiresAt = jwt.NewNumericDate(time.Now().Add(ACCESS_EXPIRATION)) - c.Type = TypeAccess - c.Id = user.Id - token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) - - // Sign and get the complete encoded token as a string using the secret - tokenString, err := token.SignedString(config.AccessSecret()) - if err != nil { - log.Println(err) - return "" - } - - return tokenString -} - -func GenerateRefresh(user *db.UserSchema) string { - c := BuildClaims(user) - c.ExpiresAt = jwt.NewNumericDate(time.Now().Add(REFRESH_EXPIRATION)) - c.Type = TypeRefresh - token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) - - // Sign and get the complete encoded token as a string using the secret - tokenString, err := token.SignedString(config.RefreshSecret()) - if err != nil { - log.Println(err) - return "" - } - - return tokenString -} - -func GenerateVerifyEmail(user *db.UserSchema) string { - c := &Claims{} - c.Id = user.Id - c.Email = user.Email - c.IssuedAt = jwt.NewNumericDate(time.Now()) - c.NotBefore = jwt.NewNumericDate(time.Now()) - c.Issuer = config.JwtIssuer() - c.Audience = config.JwtAudience() - c.ExpiresAt = jwt.NewNumericDate(time.Now().Add(VERIFY_EMAIL_EXPIRATION)) - c.Type = TypeVerifyEmail - c.Scope = "verify-email" - token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) - - // Sign and get the complete encoded token as a string using the secret - tokenString, err := token.SignedString(config.EmailTokenSecret()) - if err != nil { - log.Println(err) - return "" - } - - return tokenString -} diff --git a/server/token/keys_test.go b/server/token/keys_test.go deleted file mode 100644 index 3f37336..0000000 --- a/server/token/keys_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package token_test - -import ( - "testing" - - "github.com/mnrva-dev/owltier.com/server/db" - "github.com/mnrva-dev/owltier.com/server/token" -) - -var ts string - -// TODO Write actual unit tests - -func TestMain(m *testing.M) { - ts = token.GenerateAccess(&db.UserSchema{ - Id: "id_1234", - Username: "myusername", - Email: "user@example.com", - Scope: "admin", - Policies: []string{"policy1", "policy2"}, - }) - m.Run() -} -func TestAccessIdentity(t *testing.T) { - if ts == "" { - t.Fatal("No token was created") - } - c, err := token.ValidateAccess(ts) - if err != nil { - t.Errorf("Expected valid token, got %v", err) - } - if c.Email != "user@example.com" || c.Id != "id_1234" || c.Username != "myusername" { - t.Errorf("Unexpected identity, got %v", c) - } -} -func TestAccessScope(t *testing.T) { - c, err := token.ValidateAccess(ts) - if err != nil { - t.Errorf("Expected valid token, got %v", err) - } - if c.Scope != "admin" { - t.Errorf("Unexpected scope, expected %v got %v", "admin", c.Scope) - } - otherts := token.GenerateAccess(&db.UserSchema{ - Id: "id_1234", - Username: "myusername", - Email: "user@example.com", - Policies: []string{"policy1", "policy2"}, - }) - c, err = token.ValidateAccess(otherts) - if err != nil { - t.Errorf("Expected valid token, got %v", err) - } - if c.Scope != "default" { - t.Errorf("Unexpected scope, expected %v got %v", "default", c.Scope) - } -} -func TestAccessPolicies(t *testing.T) { - ts := token.GenerateAccess(&db.UserSchema{ - Id: "id_1234", - Username: "myusername", - Email: "user@example.com", - Scope: "admin", - Policies: []string{"policy1", "policy2"}, - }) - if ts == "" { - t.Error("No token was created") - } - c, err := token.ValidateAccess(ts) - if err != nil { - t.Errorf("Expected valid token, got %v", err) - } - if len(c.Policies) != 2 || c.Policies[0] != "policy1" || c.Policies[1] != "policy2" { - t.Errorf("Unexpected policies, got %v", c.Policies) - } -} - -func TestRefresh(t *testing.T) { - ts := token.GenerateRefresh(&db.UserSchema{ - Id: "12345", - }) - if ts == "" { - t.Error("No token was created") - } - c, err := token.ValidateRefresh(ts) - if err != nil { - t.Errorf("Expected valid token, got %v", err) - } - if c.Id != "12345" { - t.Errorf("Unexpected identity, got %v", c) - } -} diff --git a/server/token/keytypes.go b/server/token/keytypes.go deleted file mode 100644 index 30133d3..0000000 --- a/server/token/keytypes.go +++ /dev/null @@ -1,7 +0,0 @@ -package token - -const ( - TypeRefresh = "Refresh" - TypeAccess = "Access" - TypeVerifyEmail = "VerifyEmail" -) diff --git a/server/token/validate.go b/server/token/validate.go deleted file mode 100644 index 7491c8e..0000000 --- a/server/token/validate.go +++ /dev/null @@ -1,66 +0,0 @@ -package token - -import ( - "fmt" - - "github.com/golang-jwt/jwt/v5" - "github.com/mnrva-dev/owltier.com/server/config" - "golang.org/x/exp/slices" -) - -func ValidateAccess(tokenString string) (*Claims, error) { - token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { - return config.AccessSecret(), nil - }) - - if claims, ok := token.Claims.(*Claims); ok { - if !token.Valid { - return nil, fmt.Errorf("token is not valid") - } - if !slices.Contains(claims.Audience, "https://gosuimg.com") { - return nil, fmt.Errorf("unexpected audience value: %v", claims.Audience) - } - if claims.Type != "Access" { - return nil, fmt.Errorf("Unexpected token type: %v", claims.Type) - } - return claims, nil - } else { - return nil, err - } -} - -func ValidateRefresh(tokenString string) (*Claims, error) { - token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { - return config.RefreshSecret(), nil - }) - - if claims, ok := token.Claims.(*Claims); ok && token.Valid { - if !slices.Contains(claims.Audience, "https://gosuimg.com") { - return &Claims{}, fmt.Errorf("unexpected audience value: %v", claims.Audience) - } - if claims.Type != "Refresh" { - return &Claims{}, fmt.Errorf("Unexpected token type: %v", claims.Type) - } - return claims, nil - } else { - return &Claims{}, err - } -} - -func ValidateVerifyEmail(tokenString string) (*Claims, error) { - token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { - return config.RefreshSecret(), nil - }) - - if claims, ok := token.Claims.(*Claims); ok && token.Valid { - if !slices.Contains(claims.Audience, "https://gosuimg.com") { - return &Claims{}, fmt.Errorf("unexpected audience value: %v", claims.Audience) - } - if claims.Type != "VerifyEmail" { - return &Claims{}, fmt.Errorf("Unexpected token type: %v", claims.Type) - } - return claims, nil - } else { - return &Claims{}, err - } -}