Skip to content

Commit

Permalink
feat(server/auth): define unary server interceptor (#1110)
Browse files Browse the repository at this point in the history
chore: empty commit to kick ci

fix(auth/sql): add auth package qualifier when calling ListWithMethod

chore(auth/sql): remove duplicate imports

chore(auth/sql): adjust import naming to be consistent

fix(sq/auth): ensure order defaults to ascending

fix(sql): truncate timestamps to microseconds when inserting into database

fix(sql): use precision 6 for mysql timestamp columns
  • Loading branch information
GeorgeMac committed Nov 8, 2022
1 parent 7f70991 commit 1897f17
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 138 deletions.
6 changes: 3 additions & 3 deletions config/migrations/mysql/2_create_table_authentications.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ CREATE TABLE IF NOT EXISTS authentications (
hashed_client_token VARCHAR(255) UNIQUE NOT NULL,
method INTEGER DEFAULT 0 NOT NULL,
metadata TEXT,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
expires_at TIMESTAMP(6),
created_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL,
updated_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL,
PRIMARY KEY (`id`)
);

Expand Down
4 changes: 2 additions & 2 deletions internal/server/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func GetAuthenticationFrom(ctx context.Context) *authrpc.Authentication {
return auth.(*authrpc.Authentication)
}

// UnaryInterceptor is a grpc.UnaryServerInterceptor, which extracts a clientToken found
// strored within the authorization field on the incoming request metadata.
// UnaryInterceptor is a grpc.UnaryServerInterceptor which extracts a clientToken found
// within the authorization field on the incoming requests metadata.
// The fields value is expected to be in the form "Bearer <clientToken>".
func UnaryInterceptor(logger *zap.Logger, authenticator Authenticator) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
Expand Down
48 changes: 25 additions & 23 deletions internal/storage/auth/sql/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import (
"context"
"fmt"
"strconv"
"time"

"github.com/Masterminds/squirrel"
sq "github.com/Masterminds/squirrel"

"github.com/gofrs/uuid"
"go.flipt.io/flipt/internal/storage"
"go.flipt.io/flipt/internal/storage/auth"
fliptsql "go.flipt.io/flipt/internal/storage/sql"
storageauth "go.flipt.io/flipt/internal/storage/auth"
storagesql "go.flipt.io/flipt/internal/storage/sql"
rpcauth "go.flipt.io/flipt/rpc/flipt/auth"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
Expand All @@ -21,7 +21,7 @@ import (
// based relational database systems.
type Store struct {
logger *zap.Logger
driver fliptsql.Driver
driver storagesql.Driver
builder sq.StatementBuilderType

now func() *timestamppb.Timestamp
Expand All @@ -35,16 +35,21 @@ type Option func(*Store)

// NewStore constructs and configures a new instance of *Store.
// Queries are issued to the database via the provided statement builder.
func NewStore(driver fliptsql.Driver, builder sq.StatementBuilderType, logger *zap.Logger, opts ...Option) *Store {
func NewStore(driver storagesql.Driver, builder sq.StatementBuilderType, logger *zap.Logger, opts ...Option) *Store {
store := &Store{
logger: logger,
driver: driver,
builder: builder,
now: timestamppb.Now,
now: func() *timestamppb.Timestamp {
// we truncate timestampts to the microsecond to support Postgres/MySQL
// the lowest common denominators in terms of timestamp precision
now := time.Now().UTC().Truncate(time.Microsecond)
return timestamppb.New(now)
},
generateID: func() string {
return uuid.Must(uuid.NewV4()).String()
},
generateToken: auth.GenerateRandomToken,
generateToken: storageauth.GenerateRandomToken,
}

for _, opt := range opts {
Expand Down Expand Up @@ -97,7 +102,7 @@ func (s *Store) CreateAuthentication(ctx context.Context, r *storage.CreateAuthe
}
)

hashedToken, err := auth.HashClientToken(clientToken)
hashedToken, err := storageauth.HashClientToken(clientToken)
if err != nil {
return "", nil, fmt.Errorf("creating authentication: %w", err)
}
Expand All @@ -116,10 +121,10 @@ func (s *Store) CreateAuthentication(ctx context.Context, r *storage.CreateAuthe
&authentication.Id,
&hashedToken,
&authentication.Method,
&fliptsql.JSONField[map[string]string]{T: authentication.Metadata},
&fliptsql.NullableTimestamp{Timestamp: authentication.ExpiresAt},
&fliptsql.Timestamp{Timestamp: authentication.CreatedAt},
&fliptsql.Timestamp{Timestamp: authentication.UpdatedAt},
&storagesql.JSONField[map[string]string]{T: authentication.Metadata},
&storagesql.NullableTimestamp{Timestamp: authentication.ExpiresAt},
&storagesql.Timestamp{Timestamp: authentication.CreatedAt},
&storagesql.Timestamp{Timestamp: authentication.UpdatedAt},
).
ExecContext(ctx); err != nil {
return "", nil, fmt.Errorf(
Expand All @@ -137,7 +142,7 @@ func (s *Store) CreateAuthentication(ctx context.Context, r *storage.CreateAuthe
// Given a row is present for the hash of the clientToken then materialize into an Authentication.
// Else, given it cannot be located, a storage.ErrNotFound error is wrapped and returned instead.
func (s *Store) GetAuthenticationByClientToken(ctx context.Context, clientToken string) (*rpcauth.Authentication, error) {
hashedToken, err := auth.HashClientToken(clientToken)
hashedToken, err := storageauth.HashClientToken(clientToken)
if err != nil {
return nil, fmt.Errorf("getting authentication by token: %w", err)
}
Expand Down Expand Up @@ -191,16 +196,13 @@ func (s *Store) ListAuthentications(ctx context.Context, req *storage.ListReques
"updated_at",
).
From("authentications").
Limit(req.QueryParams.Limit + 1)
Limit(req.QueryParams.Limit + 1).
OrderBy(fmt.Sprintf("created_at %s", req.QueryParams.Order))

if req.Predicate.Method != nil {
query = query.Where(sq.Eq{"method": *req.Predicate.Method})
}

if req.QueryParams.Order != storage.OrderAsc {
query = query.OrderBy(fmt.Sprintf("created_at %s", req.QueryParams.Order))
}

var offset int
if v, err := strconv.ParseInt(req.QueryParams.PageToken, 10, 64); err == nil {
offset = int(v)
Expand Down Expand Up @@ -237,18 +239,18 @@ func (s *Store) ListAuthentications(ctx context.Context, req *storage.ListReques
return
}

func (s *Store) scanAuthentication(scanner squirrel.RowScanner, authentication *rpcauth.Authentication) error {
func (s *Store) scanAuthentication(scanner sq.RowScanner, authentication *rpcauth.Authentication) error {
var (
expiresAt fliptsql.NullableTimestamp
createdAt fliptsql.Timestamp
updatedAt fliptsql.Timestamp
expiresAt storagesql.NullableTimestamp
createdAt storagesql.Timestamp
updatedAt storagesql.Timestamp
)

if err := scanner.
Scan(
&authentication.Id,
&authentication.Method,
&fliptsql.JSONField[*map[string]string]{T: &authentication.Metadata},
&storagesql.JSONField[*map[string]string]{T: &authentication.Metadata},
&expiresAt,
&createdAt,
&updatedAt,
Expand Down
Loading

0 comments on commit 1897f17

Please sign in to comment.