You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-07-17 01:42:36 +02:00
Completed enough code gen for project ATM
This commit is contained in:
@ -46,15 +46,15 @@ func mapRowsTo{{ $.Model.Name }}(rows *sql.Rows) (*{{ $.Model.Name }}, error) {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
return &m, nil
|
||||
}
|
||||
{{ end }}
|
||||
{{ define "ACL"}}
|
||||
{{ $hasAccountId := (StringListHasValue $.Model.ColumnNames "account_id") }}
|
||||
{{ $hasAccountID := (StringListHasValue $.Model.ColumnNames "account_id") }}
|
||||
// CanRead{{ $.Model.Name }} determines if claims has the authority to access the specified {{ FormatCamelLowerTitle $.Model.Name}} by {{ $.Model.PrimaryColumn }}.
|
||||
func CanRead{{ $.Model.Name }}(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, {{ FormatCamelLower $.Model.PrimaryField }} string) error {
|
||||
|
||||
{{ if $hasAccountId }}
|
||||
{{ if $hasAccountID }}
|
||||
// If the request has claims from a specific {{ FormatCamelLower $.Model.Name }}, ensure that the claims
|
||||
// has the correct access to the {{ FormatCamelLower $.Model.Name }}.
|
||||
if claims.Audience != "" {
|
||||
@ -90,7 +90,7 @@ func CanRead{{ $.Model.Name }}(ctx context.Context, claims auth.Claims, dbConn *
|
||||
|
||||
// CanModify{{ $.Model.Name }} determines if claims has the authority to modify the specified {{ FormatCamelLowerTitle $.Model.Name}} by {{ $.Model.PrimaryColumn }}.
|
||||
func CanModify{{ $.Model.Name }}(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, {{ FormatCamelLower $.Model.PrimaryField }} string) error {
|
||||
err = CanRead{{ $.Model.Name }}(ctx, claims, dbConn, {{ FormatCamelLower $.Model.PrimaryField }})
|
||||
err := CanRead{{ $.Model.Name }}(ctx, claims, dbConn, {{ FormatCamelLower $.Model.PrimaryField }})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -105,7 +105,7 @@ func CanModify{{ $.Model.Name }}(ctx context.Context, claims auth.Claims, dbConn
|
||||
|
||||
// applyClaimsSelect applies a sub-query to the provided query to enforce ACL based on the claims provided.
|
||||
// 1. No claims, request is internal, no ACL applied
|
||||
{{ if $hasAccountId }}
|
||||
{{ if $hasAccountID }}
|
||||
// 2. All role types can access their user ID
|
||||
{{ end }}
|
||||
func applyClaimsSelect(ctx context.Context, claims auth.Claims, query *sqlbuilder.SelectBuilder) error {
|
||||
@ -114,7 +114,7 @@ func applyClaimsSelect(ctx context.Context, claims auth.Claims, query *sqlbuilde
|
||||
return nil
|
||||
}
|
||||
|
||||
{{ if $hasAccountId }}
|
||||
{{ if $hasAccountID }}
|
||||
query.Where(query.Equal("account_id", claims.Audience))
|
||||
{{ end }}
|
||||
|
||||
@ -226,9 +226,10 @@ func Read(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, {{ FormatCam
|
||||
}
|
||||
{{ end }}
|
||||
{{ define "Create"}}
|
||||
{{ $hasAccountId := (StringListHasValue $.Model.ColumnNames "account_id") }}
|
||||
{{ $hasAccountID := (StringListHasValue $.Model.ColumnNames "account_id") }}
|
||||
{{ $reqName := (Concat $.Model.Name "CreateRequest") }}
|
||||
{{ $createFields := (index $.StructFields $reqName) }}
|
||||
{{ $reqHasAccountID := false }}{{ $reqAccountID := (index $createFields "AccountID") }}{{ if $reqAccountID }}{{ $reqHasAccountID = true }}{{ end }}
|
||||
// Create inserts a new {{ FormatCamelLowerTitle $.Model.Name }} into the database.
|
||||
func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req {{ $reqName }}, now time.Time) (*{{ $.Model.Name }}, error) {
|
||||
span, ctx := tracer.StartSpanFromContext(ctx, "internal.{{ FormatCamelLowerUnderscore $.Model.Name }}.Create")
|
||||
@ -237,18 +238,18 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req {{ $re
|
||||
if claims.Audience != "" {
|
||||
// Admin users can update {{ FormatCamelPluralTitleLower $.Model.Name }} they have access to.
|
||||
if !claims.HasRole(auth.RoleAdmin) {
|
||||
return errors.WithStack(ErrForbidden)
|
||||
return nil, errors.WithStack(ErrForbidden)
|
||||
}
|
||||
|
||||
{{ if $hasAccountId }}
|
||||
if req.AccountId != "" {
|
||||
{{ if $reqHasAccountID }}
|
||||
if req.AccountID != "" {
|
||||
// Request accountId must match claims.
|
||||
if req.AccountId != claims.Audience {
|
||||
return errors.WithStack(ErrForbidden)
|
||||
if req.AccountID != claims.Audience {
|
||||
return nil, errors.WithStack(ErrForbidden)
|
||||
}
|
||||
} else {
|
||||
// Set the accountId from claims.
|
||||
req.AccountId = claims.Audience
|
||||
req.AccountID = claims.Audience
|
||||
}
|
||||
{{ end }}
|
||||
}
|
||||
@ -256,7 +257,7 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req {{ $re
|
||||
v := validator.New()
|
||||
|
||||
// Validate the request.
|
||||
err = v.Struct(req)
|
||||
err := v.Struct(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -281,6 +282,13 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req {{ $re
|
||||
{{ end }}{{ end }}
|
||||
}
|
||||
|
||||
{{ if and (not $reqHasAccountID) ($hasAccountID) }}
|
||||
// Set the accountId from claims.
|
||||
if claims.Audience != "" && m.AccountID == "" {
|
||||
req.AccountID = claims.Audience
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range $fk, $f := $createFields }}{{ $required := (FieldTagHasOption $f "validate" "required") }}{{ if not $required }}
|
||||
if req.{{ $f.FieldName }} != nil {
|
||||
{{ if eq $f.FieldType "sql.NullString" }}
|
||||
@ -315,7 +323,7 @@ func Create(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req {{ $re
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
return &m, nil
|
||||
}
|
||||
{{ end }}
|
||||
{{ define "Update"}}
|
||||
|
@ -1,19 +1,11 @@
|
||||
{{ define "imports"}}
|
||||
import (
|
||||
"github.com/lib/pq"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"{{ $.GoSrcPath }}/internal/platform/auth"
|
||||
"{{ $.GoSrcPath }}/internal/platform/tests"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
{{ end }}
|
||||
{{ define "Globals"}}
|
||||
@ -33,7 +25,7 @@ func testMain(m *testing.M) int {
|
||||
{{ define "TestFindRequestQuery"}}
|
||||
// TestFindRequestQuery validates findRequestQuery
|
||||
func TestFindRequestQuery(t *testing.T) {
|
||||
where := "name = ? or address1 = ?"
|
||||
where := "field1 = ? or field2 = ?"
|
||||
var (
|
||||
limit uint = 12
|
||||
offset uint = 34
|
||||
@ -52,7 +44,7 @@ func TestFindRequestQuery(t *testing.T) {
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
expected := "SELECT " + accountMapColumns + " FROM " + accountTableName + " WHERE (name = ? or address1 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34"
|
||||
expected := "SELECT " + {{ FormatCamelLower $.Model.Name }}MapColumns + " FROM " + {{ FormatCamelLower $.Model.Name }}TableName + " WHERE (field1 = ? or field2 = ?) ORDER BY id asc, created_at desc LIMIT 12 OFFSET 34"
|
||||
|
||||
res, args := findRequestQuery(req)
|
||||
|
||||
@ -63,4 +55,77 @@ func TestFindRequestQuery(t *testing.T) {
|
||||
t.Fatalf("\t%s\tExpected result query to match. Diff:\n%s", tests.Failed, diff)
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
{{ define "TestApplyClaimsSelect"}}
|
||||
// TestApplyClaimsSelect applyClaimsSelect
|
||||
func TestApplyClaimsSelect(t *testing.T) {
|
||||
var claimTests = []struct {
|
||||
name string
|
||||
claims auth.Claims
|
||||
expectedSql string
|
||||
error error
|
||||
}{
|
||||
{"EmptyClaims",
|
||||
auth.Claims{},
|
||||
"SELECT " + {{ FormatCamelLower $.Model.Name }}MapColumns + " FROM " + {{ FormatCamelLower $.Model.Name }}TableName,
|
||||
nil,
|
||||
},
|
||||
{"RoleAccount",
|
||||
auth.Claims{
|
||||
Roles: []string{auth.RoleAdmin},
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
Subject: "user1",
|
||||
Audience: "acc1",
|
||||
},
|
||||
},
|
||||
"SELECT " + {{ FormatCamelLower $.Model.Name }}MapColumns + " FROM " + {{ FormatCamelLower $.Model.Name }}TableName + " WHERE account_id = 'acc1'",
|
||||
nil,
|
||||
},
|
||||
{"RoleAdmin",
|
||||
auth.Claims{
|
||||
Roles: []string{auth.RoleAdmin},
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
Subject: "user1",
|
||||
Audience: "acc1",
|
||||
},
|
||||
},
|
||||
"SELECT " + {{ FormatCamelLower $.Model.Name }}MapColumns + " FROM " + {{ FormatCamelLower $.Model.Name }}TableName + " WHERE account_id = 'acc1'",
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
t.Log("Given the need to validate ACLs are enforced by claims to a select query.")
|
||||
{
|
||||
for i, tt := range claimTests {
|
||||
t.Logf("\tTest: %d\tWhen running test: %s", i, tt.name)
|
||||
{
|
||||
ctx := tests.Context()
|
||||
|
||||
query := selectQuery()
|
||||
|
||||
err := applyClaimsSelect(ctx, tt.claims, query)
|
||||
if err != tt.error {
|
||||
t.Logf("\t\tGot : %+v", err)
|
||||
t.Logf("\t\tWant: %+v", tt.error)
|
||||
t.Fatalf("\t%s\tapplyClaimsSelect failed.", tests.Failed)
|
||||
}
|
||||
|
||||
sql, args := query.Build()
|
||||
|
||||
// Use mysql flavor so placeholders will get replaced for comparison.
|
||||
sql, err = sqlbuilder.MySQL.Interpolate(sql, args)
|
||||
if err != nil {
|
||||
t.Log("\t\tGot :", err)
|
||||
t.Fatalf("\t%s\tapplyClaimsSelect failed.", tests.Failed)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(sql, tt.expectedSql); diff != "" {
|
||||
t.Fatalf("\t%s\tExpected result query to match. Diff:\n%s", tests.Failed, diff)
|
||||
}
|
||||
|
||||
t.Logf("\t%s\tapplyClaimsSelect ok.", tests.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{{ end }}
|
@ -38,7 +38,7 @@ type {{ $f.FieldType }} string
|
||||
const (
|
||||
{{ range $evk, $ev := $f.DbColumn.EnumValues }}
|
||||
// {{ $f.FieldType }}_{{ FormatCamel $ev }} defines the {{ $f.ColumnName }} of {{ $ev }} for {{ FormatCamelLowerTitle $.Model.Name }}.
|
||||
{{ $f.FieldType }}_{{ FormatCamel $ev }}{{ $f.FieldType }} = "{{ $ev }}"
|
||||
{{ $f.FieldType }}_{{ FormatCamel $ev }} {{ $f.FieldType }} = "{{ $ev }}"
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user