feature (events): Classify auth method request/resp messages for audit events. (#1640)
* refactor (oidc): Stop emitting error for not found token id Stop emitting errors when authtoken repo.IssueAuthToken is called and there's if no pending token. It's not an "normal" state and not an error condition. * feature (audit tagging): Add tags for auth method requests/responses Including testing functions for asserting audit events created when a service is called
This commit is contained in:
20 files changed
+452
-74
No files matched your search
@@ -30,7 +30,7 @@ type GetAuthMethodRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
func (x *GetAuthMethodRequest) Reset() {
|
||||
@@ -281,7 +281,7 @@ type CreateAuthMethodResponse struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"`
|
||||
Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
Item *authmethods.AuthMethod `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"`
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ type UpdateAuthMethodRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
Item *authmethods.AuthMethod `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"`
|
||||
UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"`
|
||||
}
|
||||
@@ -591,7 +591,7 @@ type ChangeStateRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// Version is used to ensure this resource has not changed.
|
||||
// The mutation will fail if the version does not match the latest known good version.
|
||||
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
|
||||
@@ -876,7 +876,7 @@ type AuthenticateRequest struct {
|
||||
AuthMethodId string `protobuf:"bytes,1,opt,name=auth_method_id,proto3" json:"auth_method_id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// This can be "cookie" or "token". If not provided, "token" will be used. "cookie" activates a split-cookie method where the token is split partially between http-only and regular cookies in order
|
||||
// to keep it safe from rogue JS in the browser.
|
||||
TokenType string `protobuf:"bytes,2,opt,name=token_type,proto3" json:"token_type,omitempty"`
|
||||
TokenType string `protobuf:"bytes,2,opt,name=token_type,proto3" json:"token_type,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
// Attributes are passed to the Auth Method; the valid keys and values depend on the type of Auth Method as well as the command.
|
||||
Attributes *structpb.Struct `protobuf:"bytes,4,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
||||
// The command to perform.
|
||||
|
||||
@@ -54,6 +54,22 @@ func (req *AuthenticateResponse) Tags() ([]encrypt.PointerTag, error) {
|
||||
Pointer: "/Attributes/Fields/user_id",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/status",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/auth_url",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/token_id",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/final_redirect_url",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
// secret fields
|
||||
{
|
||||
Pointer: "/Attributes/Fields/token",
|
||||
@@ -61,3 +77,54 @@ func (req *AuthenticateResponse) Tags() ([]encrypt.PointerTag, error) {
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Tags implements the encrypt.Taggable interface which allows
|
||||
// AuthenticateRequest Attributes to be classified for the encrypt filter.
|
||||
func (req *AuthenticateRequest) Tags() ([]encrypt.PointerTag, error) {
|
||||
if req.Attributes == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return []encrypt.PointerTag{
|
||||
// public fields
|
||||
{
|
||||
Pointer: "/Attributes/Fields/login_name",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/auth_url",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/token_id",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/state",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
// secret fields
|
||||
{
|
||||
Pointer: "/Attributes/Fields/password",
|
||||
Classification: encrypt.SecretClassification,
|
||||
},
|
||||
{
|
||||
Pointer: "/Attributes/Fields/code",
|
||||
Classification: encrypt.SecretClassification,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Tags implements the encrypt.Taggable interface which allows
|
||||
// ChangeStateRequest Attributes to be classified for the encrypt filter.
|
||||
func (req *ChangeStateRequest) Tags() ([]encrypt.PointerTag, error) {
|
||||
if req.Attributes == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return []encrypt.PointerTag{
|
||||
// public fields
|
||||
{
|
||||
Pointer: "/Attributes/Fields/state",
|
||||
Classification: encrypt.PublicClassification,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package services
|
||||
package services_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,23 +6,24 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api"
|
||||
"github.com/hashicorp/boundary/sdk/wrapper"
|
||||
"github.com/hashicorp/eventlogger"
|
||||
"github.com/hashicorp/eventlogger/filters/encrypt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
structpb "google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func TestScope_Tags(t *testing.T) {
|
||||
// TestAuthenticate_Tags will test that the response filtering aligns with the
|
||||
// AuthenticateResponse and AuthenticateResponse tags. See:
|
||||
// internal/tests/api/authmethods/authenticate_test.go TestAuthenticate where
|
||||
// the audit events produced using these tags is unit tested.
|
||||
func TestAuthenticate_Tags(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
wrapper := wrapper.TestWrapper(t)
|
||||
testEncryptingFilter := &encrypt.Filter{
|
||||
Wrapper: wrapper,
|
||||
HmacSalt: []byte("salt"),
|
||||
HmacInfo: []byte("info"),
|
||||
}
|
||||
testEncryptingFilter := api.NewEncryptFilter(t, wrapper)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -30,11 +31,11 @@ func TestScope_Tags(t *testing.T) {
|
||||
wantEvent *eventlogger.Event
|
||||
}{
|
||||
{
|
||||
name: "validate-filtering",
|
||||
name: "validate-response-filtering",
|
||||
testEvent: &eventlogger.Event{
|
||||
Type: "test",
|
||||
CreatedAt: now,
|
||||
Payload: &AuthenticateResponse{
|
||||
Payload: &services.AuthenticateResponse{
|
||||
Command: "public-command",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -49,6 +50,10 @@ func TestScope_Tags(t *testing.T) {
|
||||
"token_type": structpb.NewStringValue("public-token_type"),
|
||||
"updated_time": structpb.NewStringValue("public-updated_time"),
|
||||
"user_id": structpb.NewStringValue("public-user_id"),
|
||||
"status": structpb.NewStringValue("public-status"),
|
||||
"auth_url": structpb.NewStringValue("public-auth_url"),
|
||||
"token_id": structpb.NewStringValue("public-token_id"),
|
||||
"final_redirect_url": structpb.NewStringValue("public-final_redirect_url"),
|
||||
"token": structpb.NewStringValue("secret-token"),
|
||||
},
|
||||
},
|
||||
@@ -57,7 +62,7 @@ func TestScope_Tags(t *testing.T) {
|
||||
wantEvent: &eventlogger.Event{
|
||||
Type: "test",
|
||||
CreatedAt: now,
|
||||
Payload: &AuthenticateResponse{
|
||||
Payload: &services.AuthenticateResponse{
|
||||
Command: "public-command",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
@@ -72,12 +77,57 @@ func TestScope_Tags(t *testing.T) {
|
||||
"token_type": structpb.NewStringValue("public-token_type"),
|
||||
"updated_time": structpb.NewStringValue("public-updated_time"),
|
||||
"user_id": structpb.NewStringValue("public-user_id"),
|
||||
"status": structpb.NewStringValue("public-status"),
|
||||
"auth_url": structpb.NewStringValue("public-auth_url"),
|
||||
"token_id": structpb.NewStringValue("public-token_id"),
|
||||
"final_redirect_url": structpb.NewStringValue("public-final_redirect_url"),
|
||||
"token": structpb.NewStringValue("<REDACTED>"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validate-request-filtering",
|
||||
testEvent: &eventlogger.Event{
|
||||
Type: "test",
|
||||
CreatedAt: now,
|
||||
Payload: &services.AuthenticateRequest{
|
||||
AuthMethodId: "public-auth-method-id",
|
||||
TokenType: "public-token-type",
|
||||
Command: "public-command",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"login_name": structpb.NewStringValue("public-login_name"),
|
||||
"auth_url": structpb.NewStringValue("public-auth_url"),
|
||||
"token_id": structpb.NewStringValue("public-token_id"),
|
||||
"state": structpb.NewStringValue("public-state"),
|
||||
"password": structpb.NewStringValue("secret-password"),
|
||||
"code": structpb.NewStringValue("secret-code"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantEvent: &eventlogger.Event{
|
||||
Type: "test",
|
||||
CreatedAt: now,
|
||||
Payload: &services.AuthenticateRequest{
|
||||
AuthMethodId: "public-auth-method-id",
|
||||
TokenType: "public-token-type",
|
||||
Command: "public-command",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"login_name": structpb.NewStringValue("public-login_name"),
|
||||
"auth_url": structpb.NewStringValue("public-auth_url"),
|
||||
"token_id": structpb.NewStringValue("public-token_id"),
|
||||
"state": structpb.NewStringValue("public-state"),
|
||||
"password": structpb.NewStringValue("<REDACTED>"),
|
||||
"code": structpb.NewStringValue("<REDACTED>"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user