OSS Port: Activity log breakdowns (#14160)

* Activity log breakdowns

* s/path/namespace_path

* Remove ent specific tests
This commit is contained in:
Vishal Nayak 2022-02-18 13:01:28 -05:00 committed by GitHub
parent 72da792112
commit 6898e038ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 904 additions and 435 deletions

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.19.3
// protoc v3.17.3
// source: vault/activity/activity_log.proto
package activity
@ -37,6 +37,8 @@ type EntityRecord struct {
// non_entity records whether the given EntityRecord is
// for a TWE or an entity-bound token.
NonEntity bool `protobuf:"varint,4,opt,name=non_entity,json=nonEntity,proto3" json:"non_entity,omitempty"`
// MountAccessor is the path used by client to perform login
MountAccessor string `protobuf:"bytes,5,opt,name=mount_accessor,json=mountAccessor,proto3" json:"mount_accessor,omitempty"`
}
func (x *EntityRecord) Reset() {
@ -99,6 +101,13 @@ func (x *EntityRecord) GetNonEntity() bool {
return false
}
func (x *EntityRecord) GetMountAccessor() string {
if x != nil {
return x.MountAccessor
}
return ""
}
type LogFragment struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -306,7 +315,7 @@ var File_vault_activity_activity_log_proto protoreflect.FileDescriptor
var file_vault_activity_activity_log_proto_rawDesc = []byte{
0x0a, 0x21, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79,
0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x8b, 0x01,
0x6f, 0x74, 0x6f, 0x12, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0xb2, 0x01,
0x0a, 0x0c, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b,
0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e,
@ -315,44 +324,47 @@ var file_vault_activity_activity_log_proto_rawDesc = []byte{
0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a,
0x6e, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x52, 0x09, 0x6e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x86, 0x02, 0x0a, 0x0b,
0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x6f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69,
0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69,
0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52,
0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x11, 0x6e, 0x6f, 0x6e, 0x5f,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x4c,
0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x6e, 0x45, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
0x0f, 0x6e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73,
0x1a, 0x42, 0x0a, 0x14, 0x4e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b,
0x65, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x3a, 0x02, 0x38, 0x01, 0x22, 0x45, 0x0a, 0x11, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63,
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x63, 0x74,
0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x63, 0x6f,
0x72, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0a,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x5f, 0x0a, 0x15, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x63, 0x74, 0x69,
0x76, 0x69, 0x74, 0x79, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2e,
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79,
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x1a, 0x45, 0x0a, 0x17, 0x43,
0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49,
0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x61, 0x63,
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x52, 0x09, 0x6e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x6d,
0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
0x6f, 0x72, 0x22, 0x86, 0x02, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65,
0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e,
0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x72,
0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a,
0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16,
0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12,
0x56, 0x0a, 0x11, 0x6e, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x63, 0x74,
0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x4e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x4e, 0x6f, 0x6e, 0x45, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x45, 0x0a, 0x11, 0x45,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x4c, 0x6f, 0x67,
0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x16, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74,
0x69, 0x74, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0a, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x12, 0x5f, 0x0a, 0x15, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2c, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x49, 0x64, 0x1a, 0x45, 0x0a, 0x17, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x6f, 0x67,
0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x76,
0x61, 0x75, 0x6c, 0x74, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -4,10 +4,9 @@ option go_package = "github.com/hashicorp/vault/vault/activity";
package activity;
// EntityRecord is generated the first time an client is active
// each month. This can store clients associated with entities
// or nonEntity clients, and really is a ClientRecord, not
// specifically an EntityRecord
// EntityRecord is generated the first time a client is active each month. This
// can store clients associated with entities or nonEntity clients, and really
// is a ClientRecord, not specifically an EntityRecord.
message EntityRecord {
string client_id = 1;
string namespace_id = 2;
@ -15,8 +14,11 @@ message EntityRecord {
// 4 bytes per record to store nanoseconds.
int64 timestamp = 3;
// non_entity records whether the given EntityRecord is
// for a TWE or an entity-bound token.
bool non_entity = 4;
// for a TWE or an entity-bound token.
bool non_entity = 4;
// MountAccessor is the auth mount accessor of the token used to perform the
// activity.
string mount_accessor = 5;
}
message LogFragment {

View File

@ -11,23 +11,50 @@ import (
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/timeutil"
"github.com/hashicorp/vault/sdk/helper/compressutil"
"github.com/hashicorp/vault/sdk/logical"
)
// About 66 bytes per record:
//{"namespace_id":"xxxxx","entities":1234,"non_entity_tokens":1234},
// = approx 7900 namespaces in 512KiB
// So one storage entry is fine (for now).
type NamespaceRecord struct {
NamespaceID string `json:"namespace_id"`
Entities uint64 `json:"entities"`
NonEntityTokens uint64 `json:"non_entity_tokens"`
NamespaceID string `json:"namespace_id"`
Entities uint64 `json:"entities"`
NonEntityTokens uint64 `json:"non_entity_tokens"`
Mounts []*MountRecord `json:"mounts"`
}
type CountsRecord struct {
EntityClients int `json:"entity_clients"`
NonEntityClients int `json:"non_entity_clients"`
}
type NewClientRecord struct {
Counts *CountsRecord `json:"counts"`
Namespaces []*MonthlyNamespaceRecord `json:"namespaces"`
}
type MonthRecord struct {
Timestamp int64 `json:"timestamp"`
Counts *CountsRecord `json:"counts"`
Namespaces []*MonthlyNamespaceRecord `json:"namespaces"`
NewClients *NewClientRecord `json:"new_clients"`
}
type MonthlyNamespaceRecord struct {
NamespaceID string `json:"namespace_id"`
Counts *CountsRecord `json:"counts"`
Mounts []*MountRecord `json:"mounts"`
}
type MountRecord struct {
Path string `json:"path"`
Counts *CountsRecord `json:"counts"`
}
type PrecomputedQuery struct {
StartTime time.Time
EndTime time.Time
Namespaces []*NamespaceRecord `json:"namespaces"`
Months []*MonthRecord `json:"months"`
}
type PrecomputedQueryStore struct {
@ -50,13 +77,22 @@ func (s *PrecomputedQueryStore) Put(ctx context.Context, p *PrecomputedQuery) er
if err != nil {
return err
}
err = s.view.Put(ctx, &logical.StorageEntry{
Key: path,
Value: asJson,
compressedPq, err := compressutil.Compress(asJson, &compressutil.CompressionConfig{
Type: compressutil.CompressionTypeLZ4,
})
if err != nil {
return err
}
err = s.view.Put(ctx, &logical.StorageEntry{
Key: path,
Value: compressedPq,
})
if err != nil {
return err
}
return nil
}
@ -199,12 +235,21 @@ func (s *PrecomputedQueryStore) Get(ctx context.Context, startTime, endTime time
return nil, nil
}
value, notCompressed, err := compressutil.Decompress(entry.Value)
if err != nil {
return nil, err
}
if notCompressed {
value = entry.Value
}
p := &PrecomputedQuery{}
err = json.Unmarshal(entry.Value, p)
err = json.Unmarshal(value, p)
if err != nil {
s.logger.Warn("failed query lookup at", "path", path)
return nil, err
}
return p, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ import (
"github.com/hashicorp/vault/helper/constants"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/timeutil"
"github.com/hashicorp/vault/sdk/helper/compressutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault/activity"
"github.com/mitchellh/mapstructure"
@ -121,7 +122,7 @@ func TestActivityLog_Creation_WrappingTokens(t *testing.T) {
}
id, isTWE := te.CreateClientID()
a.HandleTokenUsage(te, id, isTWE)
a.HandleTokenUsage(context.Background(), te, id, isTWE)
a.fragmentLock.Lock()
if a.fragment != nil {
@ -138,7 +139,7 @@ func TestActivityLog_Creation_WrappingTokens(t *testing.T) {
}
id, isTWE = teNew.CreateClientID()
a.HandleTokenUsage(teNew, id, isTWE)
a.HandleTokenUsage(context.Background(), teNew, id, isTWE)
a.fragmentLock.Lock()
if a.fragment != nil {
@ -229,6 +230,15 @@ func readSegmentFromStorage(t *testing.T, c *Core, path string) *logical.Storage
t.Fatalf("expected non-nil log segment at %q", path)
}
value, notCompressed, err := compressutil.Decompress(logSegment.Value)
// If the data wasn't compressed, fallback to old behavior
if !notCompressed {
logSegment.Value = value
}
if err != nil {
return logSegment
}
return logSegment
}
@ -366,12 +376,12 @@ func TestActivityLog_SaveTokensToStorageDoesNotUpdateTokenCount(t *testing.T) {
idNonEntity, isTWE := tokenEntryOne.CreateClientID()
for i := 0; i < 3; i++ {
a.HandleTokenUsage(&tokenEntryOne, idNonEntity, isTWE)
a.HandleTokenUsage(ctx, &tokenEntryOne, idNonEntity, isTWE)
}
idEntity, isTWE := entityEntry.CreateClientID()
for i := 0; i < 2; i++ {
a.HandleTokenUsage(&entityEntry, idEntity, isTWE)
a.HandleTokenUsage(ctx, &entityEntry, idEntity, isTWE)
}
err := a.saveCurrentSegmentToStorage(ctx, false)
if err != nil {
@ -398,12 +408,13 @@ func TestActivityLog_SaveTokensToStorageDoesNotUpdateTokenCount(t *testing.T) {
nonEntityTokenFlag := false
entityTokenFlag := false
for _, client := range out.Clients {
if client.NonEntity {
if client.NonEntity == true {
nonEntityTokenFlag = true
if client.ClientID != idNonEntity {
t.Fatalf("expected a client ID of %s, but saved instead %s", idNonEntity, client.ClientID)
}
} else {
}
if client.NonEntity == false {
entityTokenFlag = true
if client.ClientID != idEntity {
t.Fatalf("expected a client ID of %s, but saved instead %s", idEntity, client.ClientID)
@ -584,8 +595,8 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
}
ts := time.Now().Unix()
// First 7000 should fit in one segment
for i := 0; i < 7000; i++ {
// First 4000 should fit in one segment
for i := 0; i < 4000; i++ {
a.AddEntityToFragment(genID(i), "root", ts)
}
@ -609,12 +620,12 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
if err != nil {
t.Fatalf("could not unmarshal protobuf: %v", err)
}
if len(entityLog0.Clients) != 7000 {
t.Fatalf("unexpected entity length. Expected %d, got %d", 7000, len(entityLog0.Clients))
if len(entityLog0.Clients) != 4000 {
t.Fatalf("unexpected entity length. Expected %d, got %d", 4000, len(entityLog0.Clients))
}
// 7000 more local entities
for i := 7000; i < 14000; i++ {
// 4000 more local entities
for i := 4000; i < 8000; i++ {
a.AddEntityToFragment(genID(i), "root", ts)
}
@ -629,7 +640,7 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
Clients: make([]*activity.EntityRecord, 0, 100),
NonEntityTokens: tokens1,
}
for i := 7000; i < 7100; i++ {
for i := 4000; i < 4100; i++ {
fragment1.Clients = append(fragment1.Clients, &activity.EntityRecord{
ClientID: genID(i),
NamespaceID: "root",
@ -648,7 +659,7 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
Clients: make([]*activity.EntityRecord, 0, 100),
NonEntityTokens: tokens2,
}
for i := 14000; i < 14100; i++ {
for i := 8000; i < 8100; i++ {
fragment2.Clients = append(fragment2.Clients, &activity.EntityRecord{
ClientID: genID(i),
NamespaceID: "root",
@ -679,8 +690,8 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
if err != nil {
t.Fatalf("could not unmarshal protobuf: %v", err)
}
if len(entityLog0.Clients) != activitySegmentEntityCapacity {
t.Fatalf("unexpected entity length. Expected %d, got %d", activitySegmentEntityCapacity,
if len(entityLog0.Clients) != activitySegmentClientCapacity {
t.Fatalf("unexpected client length. Expected %d, got %d", activitySegmentClientCapacity,
len(entityLog0.Clients))
}
@ -690,7 +701,7 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
if err != nil {
t.Fatalf("could not unmarshal protobuf: %v", err)
}
expectedCount := 14100 - activitySegmentEntityCapacity
expectedCount := 8100 - activitySegmentClientCapacity
if len(entityLog1.Clients) != expectedCount {
t.Fatalf("unexpected entity length. Expected %d, got %d", expectedCount,
len(entityLog1.Clients))
@ -703,7 +714,7 @@ func TestActivityLog_MultipleFragmentsAndSegments(t *testing.T) {
for _, e := range entityLog1.Clients {
entityPresent[e.ClientID] = struct{}{}
}
for i := 0; i < 14100; i++ {
for i := 0; i < 8100; i++ {
expectedID := genID(i)
if _, present := entityPresent[expectedID]; !present {
t.Fatalf("entity ID %v = %v not present", i, expectedID)
@ -1093,7 +1104,7 @@ func (a *ActivityLog) resetEntitiesInMemory(t *testing.T) {
clientSequenceNumber: 0,
}
a.clientTracker.activeClients = make(map[string]struct{})
a.partialMonthClientTracker = make(map[string]*activity.EntityRecord)
}
func TestActivityLog_loadCurrentClientSegment(t *testing.T) {
@ -1285,7 +1296,7 @@ func TestActivityLog_loadPriorEntitySegment(t *testing.T) {
if tc.refresh {
a.l.Lock()
a.fragmentLock.Lock()
a.clientTracker.activeClients = make(map[string]struct{})
a.partialMonthClientTracker = make(map[string]*activity.EntityRecord)
a.currentSegment.startTimestamp = tc.time
a.fragmentLock.Unlock()
a.l.Unlock()
@ -1459,7 +1470,6 @@ func setupActivityRecordsInStorage(t *testing.T, base time.Time, includeEntities
} else {
WriteToStorage(t, core, ActivityLogPrefix+"entity/"+fmt.Sprint(base.Unix())+"/"+strconv.Itoa(i-1), entityData)
}
}
}
@ -1805,7 +1815,7 @@ func TestActivityLog_DeleteWorker(t *testing.T) {
func checkAPIWarnings(t *testing.T, originalEnabled, newEnabled bool, resp *logical.Response) {
t.Helper()
expectWarning := originalEnabled && !newEnabled
expectWarning := originalEnabled == true && newEnabled == false
switch {
case !expectWarning && resp != nil:
@ -3333,12 +3343,14 @@ func TestActivityLog_partialMonthClientCount(t *testing.T) {
ctx := namespace.RootContext(nil)
now := time.Now().UTC()
a, entities, tokenCounts := setupActivityRecordsInStorage(t, timeutil.StartOfMonth(now), true, true)
entities = entities[1:]
entityCounts := make(map[string]uint64)
a, clients, _ := setupActivityRecordsInStorage(t, timeutil.StartOfMonth(now), true, true)
for _, entity := range entities {
entityCounts[entity.NamespaceID] += 1
// clients[0] belongs to previous month
clients = clients[1:]
clientCounts := make(map[string]uint64)
for _, client := range clients {
clientCounts[client.NamespaceID] += 1
}
a.SetEnable(true)
@ -3349,15 +3361,6 @@ func TestActivityLog_partialMonthClientCount(t *testing.T) {
}
wg.Wait()
// entities[0] is from a previous month
partialMonthEntityCount := len(entities)
var partialMonthTokenCount int
for _, countByNS := range tokenCounts {
partialMonthTokenCount += int(countByNS)
}
expectedClientCount := partialMonthEntityCount + partialMonthTokenCount
results, err := a.partialMonthClientCount(ctx)
if err != nil {
t.Fatal(err)
@ -3371,48 +3374,35 @@ func TestActivityLog_partialMonthClientCount(t *testing.T) {
t.Fatalf("malformed results. got %v", results)
}
clientCountResponse := make([]*ClientCountInNamespace, 0)
clientCountResponse := make([]*ResponseNamespace, 0)
err = mapstructure.Decode(byNamespace, &clientCountResponse)
if err != nil {
t.Fatal(err)
}
for _, clientCount := range clientCountResponse {
if int(entityCounts[clientCount.NamespaceID]) != clientCount.Counts.DistinctEntities {
t.Errorf("bad entity count for namespace %s . expected %d, got %d", clientCount.NamespaceID, int(entityCounts[clientCount.NamespaceID]), clientCount.Counts.DistinctEntities)
if int(clientCounts[clientCount.NamespaceID]) != clientCount.Counts.DistinctEntities {
t.Errorf("bad entity count for namespace %s . expected %d, got %d", clientCount.NamespaceID, int(clientCounts[clientCount.NamespaceID]), clientCount.Counts.DistinctEntities)
}
if int(tokenCounts[clientCount.NamespaceID]) != clientCount.Counts.NonEntityTokens {
t.Errorf("bad token count for namespace %s . expected %d, got %d", clientCount.NamespaceID, int(tokenCounts[clientCount.NamespaceID]), clientCount.Counts.NonEntityTokens)
}
totalCount := int(entityCounts[clientCount.NamespaceID] + tokenCounts[clientCount.NamespaceID])
totalCount := int(clientCounts[clientCount.NamespaceID])
if totalCount != clientCount.Counts.Clients {
t.Errorf("bad client count for namespace %s . expected %d, got %d", clientCount.NamespaceID, totalCount, clientCount.Counts.Clients)
}
}
entityCount, ok := results["distinct_entities"]
distinctEntities, ok := results["distinct_entities"]
if !ok {
t.Fatalf("malformed results. got %v", results)
}
if entityCount != partialMonthEntityCount {
t.Errorf("bad entity count. expected %d, got %d", partialMonthEntityCount, entityCount)
if distinctEntities != len(clients) {
t.Errorf("bad entity count. expected %d, got %d", len(clients), distinctEntities)
}
tokenCount, ok := results["non_entity_tokens"]
if !ok {
t.Fatalf("malformed results. got %v", results)
}
if tokenCount != partialMonthTokenCount {
t.Errorf("bad token count. expected %d, got %d", partialMonthTokenCount, tokenCount)
}
clientCount, ok := results["clients"]
if !ok {
t.Fatalf("malformed results. got %v", results)
}
if clientCount != expectedClientCount {
t.Errorf("bad client count. expected %d, got %d", expectedClientCount, clientCount)
if clientCount != len(clients) {
t.Errorf("bad client count. expected %d, got %d", len(clients), clientCount)
}
}

View File

@ -2,10 +2,13 @@ package vault
import (
"context"
"fmt"
"math/rand"
"testing"
"time"
"github.com/hashicorp/vault/helper/constants"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault/activity"
)
@ -13,39 +16,50 @@ import (
// InjectActivityLogDataThisMonth populates the in-memory client store
// with some entities and tokens, overriding what was already there
// It is currently used for API integration tests
func (c *Core) InjectActivityLogDataThisMonth(t *testing.T) (map[string]uint64, map[string]uint64) {
func (c *Core) InjectActivityLogDataThisMonth(t *testing.T) map[string]*activity.EntityRecord {
t.Helper()
tokens := make(map[string]uint64, 0)
entitiesByNS := make(map[string]uint64, 0)
tokens["root"] = 5
entitiesByNS["root"] = 5
if constants.IsEnterprise {
tokens["ns0"] = 5
tokens["ns1"] = 1
tokens["ns2"] = 1
entitiesByNS["ns0"] = 1
entitiesByNS["ns1"] = 1
entitiesByNS["ns2"] = 1
}
c.activityLog.l.Lock()
defer c.activityLog.l.Unlock()
c.activityLog.fragmentLock.Lock()
defer c.activityLog.fragmentLock.Unlock()
c.activityLog.clientTracker.nonEntityCountByNamespaceID = tokens
c.activityLog.clientTracker.entityCountByNamespaceID = entitiesByNS
return entitiesByNS, tokens
for i := 0; i < 3; i++ {
er := &activity.EntityRecord{
ClientID: fmt.Sprintf("testclientid-%d", i),
NamespaceID: "root",
MountAccessor: fmt.Sprintf("testmountaccessor-%d", i),
Timestamp: time.Now().Unix(),
NonEntity: i%2 == 0,
}
c.activityLog.partialMonthClientTracker[er.ClientID] = er
}
if constants.IsEnterprise {
for j := 0; j < 2; j++ {
for i := 0; i < 2; i++ {
er := &activity.EntityRecord{
ClientID: fmt.Sprintf("ns-%d-testclientid-%d", j, i),
NamespaceID: fmt.Sprintf("ns-%d", j),
MountAccessor: fmt.Sprintf("ns-%d-testmountaccessor-%d", j, i),
Timestamp: time.Now().Unix(),
NonEntity: i%2 == 0,
}
c.activityLog.partialMonthClientTracker[er.ClientID] = er
}
}
}
return c.activityLog.partialMonthClientTracker
}
// Return the in-memory activeClients from an activity log
func (c *Core) GetActiveClients() map[string]struct{} {
out := make(map[string]struct{})
func (c *Core) GetActiveClients() map[string]*activity.EntityRecord {
out := make(map[string]*activity.EntityRecord)
c.stateLock.RLock()
c.activityLog.fragmentLock.RLock()
for k, v := range c.activityLog.clientTracker.activeClients {
for k, v := range c.activityLog.partialMonthClientTracker {
out[k] = v
}
c.activityLog.fragmentLock.RUnlock()
@ -136,7 +150,7 @@ func (a *ActivityLog) ExpectCurrentSegmentRefreshed(t *testing.T, expectedStart
if a.currentSegment.tokenCount.CountByNamespaceID == nil {
t.Errorf("expected non-nil currentSegment.tokenCount.CountByNamespaceID")
}
if a.clientTracker.activeClients == nil {
if a.partialMonthClientTracker == nil {
t.Errorf("expected non-nil activeClients")
}
if len(a.currentSegment.currentClients.Clients) > 0 {
@ -145,8 +159,8 @@ func (a *ActivityLog) ExpectCurrentSegmentRefreshed(t *testing.T, expectedStart
if len(a.currentSegment.tokenCount.CountByNamespaceID) > 0 {
t.Errorf("expected no token counts to be loaded. got: %v", a.currentSegment.tokenCount.CountByNamespaceID)
}
if len(a.clientTracker.activeClients) > 0 {
t.Errorf("expected no active entity segment to be loaded. got: %v", a.clientTracker.activeClients)
if len(a.partialMonthClientTracker) > 0 {
t.Errorf("expected no active entity segment to be loaded. got: %v", a.partialMonthClientTracker)
}
if verifyTimeNotZero {
@ -159,7 +173,7 @@ func (a *ActivityLog) ExpectCurrentSegmentRefreshed(t *testing.T, expectedStart
}
// ActiveEntitiesEqual checks that only the set of `test` exists in `active`
func ActiveEntitiesEqual(active map[string]struct{}, test []*activity.EntityRecord) bool {
func ActiveEntitiesEqual(active map[string]*activity.EntityRecord, test []*activity.EntityRecord) bool {
if len(active) != len(test) {
return false
}

View File

@ -1,119 +0,0 @@
package activity
import (
"encoding/json"
"net/http"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/credential/userpass"
"github.com/hashicorp/vault/helper/timeutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
)
func validateClientCounts(t *testing.T, resp *api.Secret, expectedEntities, expectedTokens int) {
if resp == nil {
t.Fatal("nil response")
}
if resp.Data == nil {
t.Fatal("no data")
}
expectedClients := expectedEntities + expectedTokens
entityCountJSON, ok := resp.Data["distinct_entities"]
if !ok {
t.Fatalf("no entity count: %v", resp.Data)
}
entityCount, err := entityCountJSON.(json.Number).Int64()
if err != nil {
t.Fatal(err)
}
if entityCount != int64(expectedEntities) {
t.Errorf("bad entity count. expected %v, got %v", expectedEntities, entityCount)
}
tokenCountJSON, ok := resp.Data["non_entity_tokens"]
if !ok {
t.Fatalf("no token count: %v", resp.Data)
}
tokenCount, err := tokenCountJSON.(json.Number).Int64()
if err != nil {
t.Fatal(err)
}
if tokenCount != int64(expectedTokens) {
t.Errorf("bad token count. expected %v, got %v", expectedTokens, tokenCount)
}
clientCountJSON, ok := resp.Data["clients"]
if !ok {
t.Fatalf("no client count: %v", resp.Data)
}
clientCount, err := clientCountJSON.(json.Number).Int64()
if err != nil {
t.Fatal(err)
}
if clientCount != int64(expectedClients) {
t.Errorf("bad client count. expected %v, got %v", expectedClients, clientCount)
}
}
func TestActivityLog_MonthlyActivityApi(t *testing.T) {
timeutil.SkipAtEndOfMonth(t)
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"userpass": userpass.Factory,
},
ActivityLogConfig: vault.ActivityLogCoreConfig{
ForceEnable: true,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
client := cluster.Cores[0].Client
core := cluster.Cores[0].Core
resp, err := client.Logical().Read("sys/internal/counters/activity/monthly")
if err != nil {
t.Fatal(err)
}
validateClientCounts(t, resp, 0, 0)
// inject some data and query the API
entities, tokens := core.InjectActivityLogDataThisMonth(t)
var expectedEntities int
for _, entityCount := range entities {
expectedEntities += int(entityCount)
}
var expectedTokens int
for _, tokenCount := range tokens {
expectedTokens += int(tokenCount)
}
resp, err = client.Logical().Read("sys/internal/counters/activity/monthly")
if err != nil {
t.Fatal(err)
}
validateClientCounts(t, resp, expectedEntities, expectedTokens)
// we expect a 204 if activity log is disabled
core.GetActivityLog().SetEnable(false)
req := client.NewRequest("GET", "/v1/sys/internal/counters/activity/monthly")
rawResp, err := client.RawRequest(req)
if err != nil {
t.Fatal(err)
}
if rawResp == nil {
t.Error("nil response")
}
if rawResp.StatusCode != http.StatusNoContent {
t.Errorf("expected status code %v, got %v", http.StatusNoContent, rawResp.StatusCode)
}
}

View File

@ -225,6 +225,11 @@ func (b *SystemBackend) handleActivityConfigUpdate(ctx context.Context, req *log
if config.RetentionMonths < 0 {
return logical.ErrorResponse("retention_months must be greater than or equal to 0"), logical.ErrInvalidRequest
}
if config.RetentionMonths > 36 {
config.RetentionMonths = 36
warnings = append(warnings, "retention_months cannot be greater than 36; capped to 36.")
}
}
{

View File

@ -408,7 +408,7 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
// If it is an authenticated ( i.e with vault token ) request, increment client count
if !unauth && c.activityLog != nil {
c.activityLog.HandleTokenUsage(te, clientID, isTWE)
c.activityLog.HandleTokenUsage(ctx, te, clientID, isTWE)
}
return auth, te, nil
}