Skip to content
Open
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
2 changes: 1 addition & 1 deletion internal/cmd/base/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (b *Server) CreateDevLdapAuthMethod(ctx context.Context) error {

createUserFn := func(userName, passwd string, withMembersOf []string) *gldap.Entry {
entryAttrs := map[string][]string{
"name": {userName},
"fullName": {userName},
"email": {fmt.Sprintf("%s@localhost", userName)},
"password": {passwd},
}
Expand Down
13 changes: 6 additions & 7 deletions internal/daemon/controller/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ func Verify(ctx context.Context, resourceType resource.Type, opt ...Option) (ret
resourcesToFetchGrants := append([]resource.Type{resourceType}, opts.withFetchAdditionalResourceGrants...)

var authResults perms.ACLResults
var userData template.Data
var err error
authResults, ret.UserData, ret.Scope, v.acl, ret.grants, err = v.performAuthCheck(ctx, resourcesToFetchGrants, opts.withRecursive)
if err != nil {
Expand Down Expand Up @@ -345,17 +344,17 @@ func Verify(ctx context.Context, resourceType resource.Type, opt ...Option) (ret
ea.UserInfo = &event.UserInfo{
UserId: ret.UserId,
}
if userData.Account.Id != nil {
ea.UserInfo.AuthAccountId = *userData.Account.Id
if ret.UserData.Account.Id != nil {
ea.UserInfo.AuthAccountId = *ret.UserData.Account.Id
}
ea.GrantsInfo = &event.GrantsInfo{
Grants: grants,
}
if userData.User.FullName != nil {
ea.UserName = *userData.User.FullName
if ret.UserData.User.FullName != nil {
ea.UserName = *ret.UserData.User.FullName
}
if userData.User.Email != nil {
ea.UserEmail = *userData.User.Email
if ret.UserData.User.Email != nil {
ea.UserEmail = *ret.UserData.User.Email
}

if reqInfo != nil {
Expand Down
333 changes: 332 additions & 1 deletion internal/daemon/controller/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"testing"

"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/db"
Expand Down Expand Up @@ -146,7 +148,7 @@ func TestAuthTokenAuthenticator(t *testing.T) {
}
}

func TestVerify_AuditEvent(t *testing.T) {
func TestVerify_RedactedAuth_AuditEvent(t *testing.T) {
ctx := context.Background()
eventConfig := event.TestEventerConfig(t, "Test_Verify", event.TestWithAuditSink(t))
testLock := &sync.Mutex{}
Expand Down Expand Up @@ -266,6 +268,335 @@ func TestVerify_AuditEvent(t *testing.T) {
}
}

func TestVerify_UnredactedLdapAuth_AuditEvent(t *testing.T) {
ctx := context.Background()
eventConfig := event.TestEventerConfig(t, "Test_Verify", event.TestWithAuditSink(t))

// Disable redaction so we can assert on actual values
for _, sink := range eventConfig.EventerConfig.Sinks {
if sink.AuditConfig != nil {
sink.AuditConfig.FilterOverrides = event.AuditFilterOperations{
event.SensitiveClassification: event.NoOperation,
}
}
}

testLock := &sync.Mutex{}
testLogger := hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
Name: "test",
})
require.NoError(t, event.InitSysEventer(testLogger, testLock, "Test_Verify", event.WithEventerConfig(&eventConfig.EventerConfig)))

conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
tokenRepo, err := authtoken.NewRepository(ctx, rw, rw, testKms)
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
tokenRepoFn := func() (*authtoken.Repository, error) {
return tokenRepo, nil
}
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, testKms)
}

// ok lets create a LDAP auth token
o, _ := iam.TestScopes(t, iamRepo)
databaseWrapper, err := testKms.GetWrapper(ctx, o.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
testAuthMethod := ldap.TestAuthMethod(t, conn, databaseWrapper, o.GetPublicId(), []string{"ldaps://ldap1"})
ldapAcct := ldap.TestAccount(t, conn, testAuthMethod, "freyja",
ldap.WithFullName(ctx, "Freyja Happy Doggo"),
ldap.WithEmail(ctx, "you@hellothere.com"),
)
foundLdapAcct := ldap.AllocAccount()
foundLdapAcct.PublicId = ldapAcct.GetPublicId()
require.NoError(t, rw.LookupById(ctx, foundLdapAcct))
require.Equal(t, "Freyja Happy Doggo", foundLdapAcct.FullName)
require.Equal(t, "you@hellothere.com", foundLdapAcct.Email)

user := iam.TestUser(t, iamRepo, o.GetPublicId())
_, err = iamRepo.AddUserAccounts(ctx, user.PublicId, user.Version, []string{ldapAcct.GetPublicId()})
require.NoError(t, err)

// set ldap auth method as primary
s, err := iamRepo.LookupScope(ctx, o.PublicId)
require.NoError(t, err)
iam.TestSetPrimaryAuthMethod(t, iamRepo, s, testAuthMethod.GetPublicId())

// ok lets verify we have a fullName and email to populated in the user
lookedUpUser, _, err := iamRepo.LookupUser(ctx, user.PublicId)
require.NoError(t, err)
require.Equal(t, "Freyja Happy Doggo", lookedUpUser.FullName)
require.Equal(t, "you@hellothere.com", lookedUpUser.Email)

at, err := tokenRepo.CreateAuthToken(ctx, user, ldapAcct.GetPublicId())
require.NoError(t, err)
encToken, err := authtoken.EncryptToken(context.Background(), testKms, o.GetPublicId(), at.GetPublicId(), at.GetToken())
require.NoError(t, err)

tokValue := at.GetPublicId() + "_" + encToken
jsCookieVal, httpCookieVal := tokValue[:len(tokValue)/2], tokValue[len(tokValue)/2:]

tests := []struct {
name string
headers map[string]string
cookies []http.Cookie
opt []Option
disableAuth bool
disableAuthzFails bool
wantResults VerifyResults
wantUserId string
wantNameEmail bool
}{
{
name: "bearer-token",
headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokValue)},
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: at.IamUserId,
wantNameEmail: true,
},
{
name: "split-cookie-token",
cookies: []http.Cookie{
{Name: handlers.HttpOnlyCookieName, Value: httpCookieVal},
{Name: handlers.JsVisibleCookieName, Value: jsCookieVal},
},
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: at.IamUserId,
wantNameEmail: true,
},
{
name: "no-auth-data",
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: globals.AnonymousUserId,
},
{
name: "disable-auth",
opt: []Option{WithScopeId(o.PublicId)},
disableAuth: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
req := httptest.NewRequest("GET", "http://127.0.0.1/v1/scopes/o_1", nil)
for k, v := range tt.headers {
req.Header.Set(k, v)
}
for _, c := range tt.cookies {
req.AddCookie(&c)
}

// Add values for authn/authz checking
requestInfo := authpb.RequestInfo{
Path: req.URL.Path,
Method: req.Method,
DisableAuthEntirely: tt.disableAuth,
DisableAuthzFailures: true, // we skipped giving grants so disable authz
}
requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = GetTokenFromRequest(context.TODO(), testKms, req)

ctx := NewVerifierContext(ctx, iamRepoFn, tokenRepoFn, serversRepoFn, testKms, &requestInfo)

_ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls
_ = Verify(ctx, resource.Scope, tt.opt...)
got := api.CloudEventFromFile(t, eventConfig.AuditEvents.Name())

auth, ok := got.Data.(map[string]any)["auth"].(map[string]any)
require.True(ok)

if tt.wantNameEmail {
assert.Equal("Freyja Happy Doggo", auth["name"])
assert.Equal("you@hellothere.com", auth["email"])
} else {
assert.Nil(auth["name"])
assert.Nil(auth["email"])
}

if tt.disableAuth {
assert.Equal(true, auth["disabled_auth_entirely"])
}
if tt.wantUserId != "" {
userInfo, ok := auth["user_info"].(map[string]any)
require.True(ok)
assert.Equal(tt.wantUserId, userInfo["id"])
}
})
}
}

func TestVerify_UnredactedOidcAuth_AuditEvent(t *testing.T) {
ctx := context.Background()
eventConfig := event.TestEventerConfig(t, "Test_Verify", event.TestWithAuditSink(t))

// Disable redaction so we can assert on actual values
for _, sink := range eventConfig.EventerConfig.Sinks {
if sink.AuditConfig != nil {
sink.AuditConfig.FilterOverrides = event.AuditFilterOperations{
event.SensitiveClassification: event.NoOperation,
}
}
}

testLock := &sync.Mutex{}
testLogger := hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
Name: "test",
})
require.NoError(t, event.InitSysEventer(testLogger, testLock, "Test_Verify", event.WithEventerConfig(&eventConfig.EventerConfig)))

conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
tokenRepo, err := authtoken.NewRepository(ctx, rw, rw, testKms)
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
tokenRepoFn := func() (*authtoken.Repository, error) {
return tokenRepo, nil
}
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, testKms)
}

// ok lets create a OIDC auth token
o, _ := iam.TestScopes(t, iamRepo)
databaseWrapper, err := testKms.GetWrapper(ctx, o.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
testAuthMethod := oidc.TestAuthMethod(t, conn, databaseWrapper, o.GetPublicId(), oidc.ActivePrivateState,
"alice-rp", "fido",
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://hellothere.com")[0]),
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "http://localhost")[0]),
)
oidcAcct := oidc.TestAccount(t, conn, testAuthMethod, "Freyja",
oidc.WithFullName("Freyja Good Doggo"),
oidc.WithEmail("me@hellothere.com"),
)

user := iam.TestUser(t, iamRepo, o.GetPublicId())
_, err = iamRepo.AddUserAccounts(ctx, user.PublicId, user.Version, []string{oidcAcct.GetPublicId()})
require.NoError(t, err)

s, err := iamRepo.LookupScope(ctx, o.PublicId)
require.NoError(t, err)

// set oidcg auth method as primary
iam.TestSetPrimaryAuthMethod(t, iamRepo, s, testAuthMethod.GetPublicId())

// ok lets verify we have a fullName and email to populated in the user
lookedUpUser, _, err := iamRepo.LookupUser(ctx, user.PublicId)
require.NoError(t, err)
require.Equal(t, "Freyja Good Doggo", lookedUpUser.FullName)
require.Equal(t, "me@hellothere.com", lookedUpUser.Email)

at, err := tokenRepo.CreateAuthToken(ctx, user, oidcAcct.GetPublicId())
require.NoError(t, err)
encToken, err := authtoken.EncryptToken(context.Background(), testKms, o.GetPublicId(), at.GetPublicId(), at.GetToken())
require.NoError(t, err)

tokValue := at.GetPublicId() + "_" + encToken
jsCookieVal, httpCookieVal := tokValue[:len(tokValue)/2], tokValue[len(tokValue)/2:]

tests := []struct {
name string
headers map[string]string
cookies []http.Cookie
opt []Option
disableAuth bool
disableAuthzFails bool
wantResults VerifyResults
wantUserId string
wantNameEmail bool
}{
{
name: "bearer-token",
headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokValue)},
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: at.IamUserId,
wantNameEmail: true,
},
{
name: "split-cookie-token",
cookies: []http.Cookie{
{Name: handlers.HttpOnlyCookieName, Value: httpCookieVal},
{Name: handlers.JsVisibleCookieName, Value: jsCookieVal},
},
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: at.IamUserId,
wantNameEmail: true,
},
{
name: "no-auth-data",
opt: []Option{WithScopeId(o.PublicId)},
wantUserId: globals.AnonymousUserId,
},
{
name: "disable-auth",
opt: []Option{WithScopeId(o.PublicId)},
disableAuth: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
req := httptest.NewRequest("GET", "http://127.0.0.1/v1/scopes/o_1", nil)
for k, v := range tt.headers {
req.Header.Set(k, v)
}
for _, c := range tt.cookies {
req.AddCookie(&c)
}

// Add values for authn/authz checking
requestInfo := authpb.RequestInfo{
Path: req.URL.Path,
Method: req.Method,
DisableAuthEntirely: tt.disableAuth,
DisableAuthzFailures: true, // we skipped giving grants so disable authz
}
requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = GetTokenFromRequest(context.TODO(), testKms, req)

ctx := NewVerifierContext(ctx, iamRepoFn, tokenRepoFn, serversRepoFn, testKms, &requestInfo)

_ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls
_ = Verify(ctx, resource.Scope, tt.opt...)
got := api.CloudEventFromFile(t, eventConfig.AuditEvents.Name())

auth, ok := got.Data.(map[string]any)["auth"].(map[string]any)
require.True(ok)

if tt.wantNameEmail {
assert.Equal("Freyja Good Doggo", auth["name"])
assert.Equal("me@hellothere.com", auth["email"])
} else {
assert.Nil(auth["name"])
assert.Nil(auth["email"])
}

if tt.disableAuth {
assert.Equal(true, auth["disabled_auth_entirely"])
}
if tt.wantUserId != "" {
userInfo, ok := auth["user_info"].(map[string]any)
require.True(ok)
assert.Equal(tt.wantUserId, userInfo["id"])
}
})
}
}

func TestGrantsHash(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
Expand Down
Loading
Loading