Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MB-16416: Add auth integration tests #10938

Merged
merged 13 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/auth/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type SessionManager interface {
Load(context.Context, string) (context.Context, error)
LoadAndSave(http.Handler) http.Handler
Store() scs.Store
SessionCookie() scs.SessionCookie
}

type ScsSessionManagerWrapper struct {
Expand Down Expand Up @@ -98,6 +99,10 @@ func (s ScsSessionManagerWrapper) Store() scs.Store {
return s.ScsSessionManager.Store
}

func (s ScsSessionManagerWrapper) SessionCookie() scs.SessionCookie {
return s.ScsSessionManager.Cookie
}

type AppSessionManagers struct {
Mil SessionManager
Office SessionManager
Expand Down
16 changes: 16 additions & 0 deletions pkg/factory/admin_user_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,19 @@ func GetTraitAdminUserEmail() []Customization {
},
}
}

// GetTraitActiveAdminUser sets the User and AdminUser as Active
func GetTraitActiveAdminUser() []Customization {
return []Customization{
{
Model: models.AdminUser{
Active: true,
},
},
{
Model: models.User{
Active: true,
},
},
}
}
80 changes: 69 additions & 11 deletions pkg/factory/client_cert_factory.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,99 @@
package factory

import (
"crypto/sha256"
"database/sql"
"encoding/hex"
"log"

"github.com/gobuffalo/pop/v6"
"github.com/gofrs/uuid"

"github.com/transcom/mymove/pkg/models"
"github.com/transcom/mymove/pkg/testdatagen"
)

// BuildClientCert creates a Client Certificate
// BuildClientCert creates a single ClientCert
// Params:
// - customs is a slice that will be modified by the factory
// - db can be set to nil to create a stubbed model that is not stored in DB.
func BuildClientCert(db *pop.Connection, customs []Customization, traits []Trait) models.ClientCert {
customs = setupCustomizations(customs, traits)

// Find ClientCert assertion and covert models to ClientCert
var cClientCert models.ClientCert
// default to allowing prime
defaultAllowPrime := true
if result := findValidCustomization(customs, ClientCert); result != nil {
cClientCert = result.Model.(models.ClientCert)
if result.LinkOnly {
return cClientCert
}
// if customization is provided, explicitly override it to
// allow false to override true
defaultAllowPrime = cClientCert.AllowPrime
}

// find/create user and roles
user := BuildUserAndUsersRoles(db, customs, traits)

// create the client certificate
certificate := models.ClientCert{
Subject: "CN=example-user,OU=DoD+OU=PKI+OU=CONTRACTOR,O=U.S. Government,C=US",
Sha256Digest: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
id := uuid.Must(uuid.NewV4())
s := sha256.Sum256(id.Bytes())
clientCert := models.ClientCert{
ID: id,
Sha256Digest: hex.EncodeToString(s[:]),
Subject: "/C=US/ST=DC/L=Washington/O=Truss/OU=AppClientTLS/CN=factory-" + id.String(),
UserID: user.ID,
AllowPrime: defaultAllowPrime,
}

// Overwrite the values wtih those from assetions
testdatagen.MergeModels(&certificate, cClientCert)
// Overwrite values with those from customizations
testdatagen.MergeModels(&clientCert, cClientCert)

// If db is false, it's a stub. No need to create in database
if db != nil {
mustCreate(db, &certificate)
mustCreate(db, &clientCert)
}
return clientCert
}

func BuildPrimeClientCert(db *pop.Connection) models.ClientCert {
return BuildClientCert(db, nil, []Trait{GetTraitPrimeUser})
}

return certificate
// Create dev client cert from 20191212230438_add_devlocal-mtls_client_cert.up.sql
const devlocalSha256Digest = "2c0c1fc67a294443292a9e71de0c71cc374fe310e8073f8cdc15510f6b0ef4db"
const devlocalSubject = "/C=US/ST=DC/L=Washington/O=Truss/OU=AppClientTLS/CN=devlocal"

var devlocalID = uuid.Must(uuid.FromString("190b1e07-eef8-445a-9696-5a2b49ee488d"))

// FetchOrBuildDevlocalClientCert tries fetching an existing clientCert, then falls back to creating one
func FetchOrBuildDevlocalClientCert(db *pop.Connection) models.ClientCert {
traits := []Trait{GetTraitClientCertDevlocal, GetTraitPrimeUser}
if db == nil {
return BuildClientCert(db, nil, traits)
}

var clientCert models.ClientCert
err := db.Q().Where(`sha256_digest=$1`, devlocalSha256Digest).First(&clientCert)
if err != nil && err != sql.ErrNoRows {
log.Panic(err)
} else if err == nil {
return clientCert
}

return BuildClientCert(db, nil, traits)
}

// Traits
// GetTraitOfficeUserActive sets the User and OfficeUser as Active
func GetTraitClientCertDevlocal() []Customization {
return []Customization{
{
Model: models.ClientCert{
ID: devlocalID,
Sha256Digest: devlocalSha256Digest,
Subject: devlocalSubject,
AllowPrime: true,
},
},
}
}
175 changes: 100 additions & 75 deletions pkg/factory/client_cert_factory_test.go
Original file line number Diff line number Diff line change
@@ -1,108 +1,133 @@
package factory

import (
"crypto/sha256"
"encoding/hex"

"github.com/gofrs/uuid"

"github.com/transcom/mymove/pkg/models"
"github.com/transcom/mymove/pkg/models/roles"
)

const defaultSHA256Digest string = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
const defaultSubject string = "CN=example-user,OU=DoD+OU=PKI+OU=CONTRACTOR,O=U.S. Government,C=US"
const customSubject string = "CN=custom-user,OU=DoD+OU=PKI+OU=CONTRACTOR,O=U.S. Government,C=US"

func (suite *FactorySuite) TestBuildClientCert() {
suite.Run("Successful creation of default user", func() {
// Under test: BuildClientCert
// Mocked: None
// Set up: Create a ClientCert with no customizations or traits
// Expected outcome: ClientCert should be created with default values

certificate := BuildClientCert(suite.DB(), nil, nil)
suite.Equal(defaultSHA256Digest, certificate.Sha256Digest)
suite.Equal(defaultSubject, certificate.Subject)
suite.NotNil(certificate.UserID)
suite.Run("Successful creation of default clientCert", func() {
// Under test: BuildClientCert
// Mocked: None
// Set up: Create a clientCert with no customizations or traits
// Expected outcome:ClientCert should be created with default values

// FUNCTION UNDER TEST
clientCert := BuildClientCert(suite.DB(), nil, nil)

// VALIDATE RESULTS
suite.NotEmpty(clientCert.Sha256Digest)
suite.Contains(clientCert.Subject, "OU=AppClientTLS/CN=factory-")
suite.True(clientCert.AllowPrime)
})

suite.Run("Successful creation of user with customization", func() {
// Under test: BuildClientCert
// Set up: Create a ClientCert with a customized subject and no trait
// Expected outcome: ClientCert should be created with default SHA256 digest and custom subject with no user association
certificate := BuildClientCert(suite.DB(), []Customization{
{
Model: models.ClientCert{
Subject: customSubject,
},
},
}, nil)
suite.Equal(defaultSHA256Digest, certificate.Sha256Digest)
suite.Equal(customSubject, certificate.Subject)
suite.NotNil(certificate.UserID)
suite.Run("Successful creation of prime clientCert", func() {
// Under test: BuildPrimeClientCert
// Mocked: None
// Set up: Create a clientCert with no customizations or traits
// Expected outcome:ClientCert should be created with default
// values, associated with an active user with the prime role

// FUNCTION UNDER TEST
clientCert := BuildPrimeClientCert(suite.DB())

// VALIDATE RESULTS
suite.NotEmpty(clientCert.Sha256Digest)
suite.Contains(clientCert.Subject, "OU=AppClientTLS/CN=factory-")
suite.True(clientCert.AllowPrime)
var user models.User
suite.NoError(suite.DB().Eager("Roles").Find(&user, clientCert.UserID))
suite.NotEmpty(user.Roles)
suite.Equal(1, len(user.Roles), user.Roles)
suite.Equal(roles.RoleTypePrime, user.Roles[0].RoleType)
})

suite.Run("Successful creation of user with trait", func() {
// Under test: BuildClientCert
// Set up: Create a Client Certificate with a trait
// Expected outcome: User should be created with with default SHA256,
// Subject, and an associated UserID
suite.Run("Successful creation of customized clientCert", func() {
// Under test: BuildClientCert
// Mocked: None
// Set up: Create a clientCert with customization
// Expected outcome:ClientCert should be created with customized values

// SETUP
// Create a custom clientCert to compare values
s := sha256.Sum256([]byte("custom"))
custClientCert := models.ClientCert{
Sha256Digest: hex.EncodeToString(s[:]),
Subject: "CustomSubject",
AllowPrime: false,
}

user := BuildUser(suite.DB(), nil, nil)
customUser := BuildUser(suite.DB(), nil, nil)

certificate := BuildClientCert(suite.DB(), []Customization{
// FUNCTION UNDER TEST
clientCert := BuildClientCert(suite.DB(), []Customization{
{
Model: custClientCert,
},
{
Model: user,
Model: customUser,
LinkOnly: true,
},
}, nil)
suite.Equal(defaultSHA256Digest, certificate.Sha256Digest)
suite.Equal(defaultSubject, certificate.Subject)
suite.Equal(user.ID, certificate.UserID)
})

suite.Run("Successful creation of user with both", func() {
// Under test: BuildClientCert
// Set up: Create a Client Certificate with a customized subject and active trait
// Expected outcome: Cert should be created with default Sha256Digest, custom subject, and an associated UserID

certificate := BuildClientCert(suite.DB(), []Customization{
{
Model: models.ClientCert{
Subject: customSubject,
},
}}, []Trait{
GetTraitActiveUser,
})
suite.Equal(defaultSHA256Digest, certificate.Sha256Digest)
suite.Equal(customSubject, certificate.Subject)
suite.NotNil(certificate.UserID)
// VALIDATE RESULTS
suite.Equal(custClientCert.Sha256Digest, clientCert.Sha256Digest)
suite.Equal(custClientCert.Subject, clientCert.Subject)
suite.Equal(custClientCert.AllowPrime, clientCert.AllowPrime)
suite.Equal(customUser.ID, clientCert.UserID)
})

suite.Run("Successful creation of stubbed user", func() {
// Under test: BuildClientCert
// Set up: Create a customized user, but don't pass in a db
// Expected outcome: Client Certifiate should be created with email and active status
// No user should be created in database
suite.Run("Successful return of linkOnly clientCert", func() {
// Under test: BuildClientCert
// Set up: Create a clientCert and pass in a linkOnly clientCert
// Expected outcome:No new clientCert should be created

// Check num clientCerts
precount, err := suite.DB().Count(&models.ClientCert{})
suite.NoError(err)

user := models.User{
ID: uuid.Must(uuid.NewV4()),
customCert := models.ClientCert{
ID: uuid.Must(uuid.NewV4()),
Sha256Digest: hex.EncodeToString([]byte("LinkOnly")),
Subject: "LinkOnly",
}
certificate := BuildClientCert(nil, []Customization{
{
Model: models.ClientCert{
Subject: customSubject,
},
},

clientCert := BuildClientCert(suite.DB(), []Customization{
{
Model: user,
Model: customCert,
LinkOnly: true,
},
}, nil)

suite.Equal(customSubject, certificate.Subject)
suite.Equal(user.ID, certificate.UserID)
// Count how many certificates are in the DB, no certificates should have been created.
count, err := suite.DB().Count(&models.ClientCert{})
suite.NoError(err)
suite.Equal(precount, count)
suite.NoError(err)

suite.Equal(customCert.ID, clientCert.ID)
suite.Equal(customCert.Sha256Digest, clientCert.Sha256Digest)
suite.Equal(customCert.Subject, clientCert.Subject)
})

suite.Run("Two clientCerts should not be created", func() {
// Under test: FetchOrBuildDevlocalClientCert
// Set up: Create a clientCert with no customized state or traits
// Expected outcome:Only 1 clientCert should be created
count, potentialErr := suite.DB().Where(`sha256_digest=$1`, devlocalSha256Digest).Count(&models.ClientCert{})
suite.NoError(potentialErr)
suite.Zero(count)

firstClientCert := FetchOrBuildDevlocalClientCert(suite.DB())

secondClientCert := FetchOrBuildDevlocalClientCert(suite.DB())

suite.Equal(firstClientCert.ID, secondClientCert.ID)

existingClientCertCount, err := suite.DB().Where(`sha256_digest=$1`, devlocalSha256Digest).Count(&models.ClientCert{})
suite.NoError(err)
suite.Equal(1, existingClientCertCount)
})
}
16 changes: 16 additions & 0 deletions pkg/factory/office_user_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,19 @@ func GetTraitOfficeUserWithID() []Customization {
},
}
}

// GetTraitOfficeUserActive sets the User and OfficeUser as Active
func GetTraitActiveOfficeUser() []Customization {
return []Customization{
{
Model: models.OfficeUser{
Active: true,
},
},
{
Model: models.User{
Active: true,
},
},
}
}
11 changes: 11 additions & 0 deletions pkg/factory/service_member_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,14 @@ func GetTraitServiceMemberSetIDs() []Customization {
},
}
}

// GetTraitServiceMemberUserActive sets the User as Active
func GetTraitActiveServiceMemberUser() []Customization {
return []Customization{
{
Model: models.User{
Active: true,
},
},
}
}
Loading