Skip to content

Commit

Permalink
Merge pull request #70 from KonferCA/feat/64/user-role-enum
Browse files Browse the repository at this point in the history
Feat/64/user role enum
  • Loading branch information
juancwu authored Nov 18, 2024
2 parents 7abba7c + de776eb commit 525efb4
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .sqlc/migrations/20241108045258_create_users_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ CREATE TABLE users (
-- +goose Down
-- +goose StatementBegin
DROP TABLE users;
-- +goose StatementEnd
-- +goose StatementEnd
17 changes: 17 additions & 0 deletions .sqlc/migrations/20241116191633_user_role_enum.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- +goose Up
-- +goose StatementBegin
BEGIN;

CREATE TYPE user_role AS ENUM ('admin', 'startup_owner', 'investor');

COMMIT;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
BEGIN;

DROP TYPE user_role;

COMMIT;
-- +goose StatementEnd
29 changes: 29 additions & 0 deletions .sqlc/migrations/20241116192218_alter_users_role_col.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- +goose Up
-- +goose StatementBegin
BEGIN;

ALTER TABLE users ADD COLUMN role_enum user_role NOT NULL;

UPDATE users
SET role_enum = role::user_role;

ALTER TABLE users DROP COLUMN role;
ALTER TABLE users RENAME COLUMN role_enum TO role;

COMMIT;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
BEGIN;

ALTER TABLE users ADD COLUMN role_varchar VARCHAR(50) NOT NULL;

UPDATE users
SET role_varchar = role::text;

ALTER TABLE users DROP COLUMN role;
ALTER TABLE users RENAME COLUMN role_varchar TO role;

COMMIT;
-- +goose StatementEnd
65 changes: 64 additions & 1 deletion db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions db/users.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions internal/jwt/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"time"

"github.com/KonferCA/NoKap/db"
golangJWT "github.com/golang-jwt/jwt/v5"
)

Expand All @@ -13,7 +14,7 @@ const (
)

// Generates JWT tokens for the given user. Returns the access token, refresh token and error (nil if no error)
func Generate(userID string, role string) (string, string, error) {
func Generate(userID string, role db.UserRole) (string, string, error) {
accessToken, err := generateToken(userID, role, ACCESS_TOKEN_TYPE, time.Now().Add(10*time.Minute))
if err != nil {
return "", "", err
Expand All @@ -28,7 +29,7 @@ func Generate(userID string, role string) (string, string, error) {
}

// Private helper method to generate a token.
func generateToken(userID, role, tokenType string, exp time.Time) (string, error) {
func generateToken(userID string, role db.UserRole, tokenType string, exp time.Time) (string, error) {
claims := JWTClaims{
UserID: userID,
Role: role,
Expand Down
3 changes: 2 additions & 1 deletion internal/jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"
"time"

"github.com/KonferCA/NoKap/db"
golangJWT "github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
)
Expand All @@ -14,7 +15,7 @@ func TestJWT(t *testing.T) {
os.Setenv("JWT_SECRET", "secret")

userID := "some-user-id"
role := "user"
role := db.UserRole("user")
exp := time.Now().Add(5 * time.Minute)

t.Run("generate access token", func(t *testing.T) {
Expand Down
11 changes: 7 additions & 4 deletions internal/jwt/types.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package jwt

import golangJWT "github.com/golang-jwt/jwt/v5"
import (
"github.com/KonferCA/NoKap/db"
golangJWT "github.com/golang-jwt/jwt/v5"
)

type JWTClaims struct {
UserID string `json:"user_id"`
Role string `json:"role"`
TokenType string `json:"token_type"`
UserID string `json:"user_id"`
Role db.UserRole `json:"role"`
TokenType string `json:"token_type"`
golangJWT.RegisteredClaims
}
5 changes: 3 additions & 2 deletions internal/middleware/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"testing"

"github.com/KonferCA/NoKap/db"
"github.com/KonferCA/NoKap/internal/jwt"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
Expand All @@ -24,7 +25,7 @@ func TestProtectAPIForAccessToken(t *testing.T) {

// generate valid tokens
userID := "user-id"
role := "user-role"
role := db.UserRole("user-role")
accessToken, refreshToken, err := jwt.Generate(userID, role)
assert.Nil(t, err)

Expand Down Expand Up @@ -103,7 +104,7 @@ func TestProtectAPIForRefreshToken(t *testing.T) {

// generate valid tokens
userID := "user-id"
role := "user-role"
role := db.UserRole("user-role")
accessToken, refreshToken, err := jwt.Generate(userID, role)
assert.Nil(t, err)

Expand Down
33 changes: 32 additions & 1 deletion internal/middleware/req_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"reflect"

"github.com/KonferCA/NoKap/db"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
Expand All @@ -29,7 +30,9 @@ func (rv *RequestBodyValidator) Validate(i interface{}) error {
// Creates a new request validator that can be set to an Echo instance
// and used for validating request bodies with c.Validate()
func NewRequestBodyValidator() *RequestBodyValidator {
return &RequestBodyValidator{validator: validator.New()}
v := validator.New()
v.RegisterValidation("valid_user_role", validateUserRole)
return &RequestBodyValidator{validator: v}
}

// Middleware that validates the incoming request body with the given structType.
Expand Down Expand Up @@ -57,3 +60,31 @@ func ValidateRequestBody(structType reflect.Type) echo.MiddlewareFunc {
}
}
}

// validateUserRole validates the "valid_user_role" tag using the
// the generated valid method from SQLc.
func validateUserRole(fl validator.FieldLevel) bool {
field := fl.Field()

// handle string type
if field.Kind() == reflect.String {
str := field.String()
ur := db.UserRole(str)
return ur.Valid()
}

// handle db.UserRole type
if field.Type() == reflect.TypeOf(db.UserRole("")) {
ur := field.Interface().(db.UserRole)
return ur.Valid()

}

// handle pointer to db.UserRole
if field.Type() == reflect.TypeOf((*db.UserRole)(nil)) && !field.IsNil() {
ur := field.Interface().(*db.UserRole)
return ur.Valid()
}

return false
}
Loading

0 comments on commit 525efb4

Please sign in to comment.