Skip to content

Commit b8cb89d

Browse files
Merge pull request #1151 from hashicorp/gs/add-audit-config
Add Organization Audit Configuration classes which are in limited beta
2 parents 5d5211f + 9020a82 commit b8cb89d

7 files changed

+364
-97
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
## Enhancements
4+
5+
* Adds BETA support for reading, testing and updating Organization Audit Configuration by @glennsarti-hashi [#1151](https://github.com/hashicorp/go-tfe/pull/1151)
6+
37
# v1.87.0
48

59
## Bug Fixes

entitlement_helper_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"errors"
9+
)
10+
11+
func getOrgEntitlements(client *Client, organizationName string) (*Entitlements, error) {
12+
ctx := context.Background()
13+
orgEntitlements, err := client.Organizations.ReadEntitlements(ctx, organizationName)
14+
if err != nil {
15+
return nil, err
16+
}
17+
if orgEntitlements == nil {
18+
return nil, errors.New("The organization entitlements are empty.")
19+
}
20+
return orgEntitlements, nil
21+
}
22+
23+
func hasGlobalRunTasks(client *Client, organizationName string) (bool, error) {
24+
oe, err := getOrgEntitlements(client, organizationName)
25+
if err != nil {
26+
return false, err
27+
}
28+
return oe.GlobalRunTasks, nil
29+
}
30+
31+
func hasPrivateRunTasks(client *Client, organizationName string) (bool, error) {
32+
oe, err := getOrgEntitlements(client, organizationName)
33+
if err != nil {
34+
return false, err
35+
}
36+
return oe.PrivateRunTasks, nil
37+
}
38+
39+
func hasAuditLogging(client *Client, organizationName string) (bool, error) {
40+
oe, err := getOrgEntitlements(client, organizationName)
41+
if err != nil {
42+
return false, err
43+
}
44+
return oe.AuditLogging, nil
45+
}

organization.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ type OrganizationPermissions struct {
189189
CanCreateWorkspaceMigration bool `jsonapi:"attr,can-create-workspace-migration"`
190190
CanDeployNoCodeModules bool `jsonapi:"attr,can-deploy-no-code-modules"`
191191
CanDestroy bool `jsonapi:"attr,can-destroy"`
192+
CanManageAuditing bool `jsonapi:"attr,can-manage-auditing"`
192193
CanManageNoCodeModules bool `jsonapi:"attr,can-manage-no-code-modules"`
193194
CanManageRunTasks bool `jsonapi:"attr,can-manage-run-tasks"`
194195
CanTraverse bool `jsonapi:"attr,can-traverse"`
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/url"
10+
"time"
11+
)
12+
13+
var _ OrganizationAuditConfigurations = (*organizationAuditConfigurations)(nil)
14+
15+
// OrganizationAuditConfigurations describes the configuration for auditing events for the organization.
16+
type OrganizationAuditConfigurations interface {
17+
// Read the audit configuration of an organization by its name.
18+
Read(ctx context.Context, organization string) (*OrganizationAuditConfiguration, error)
19+
20+
// Send a test audit event for an organization by its name.
21+
Test(ctx context.Context, organization string) (*OrganizationAuditConfigurationTest, error)
22+
23+
// Update the audit configuration of an organization by its name.
24+
Update(ctx context.Context, organization string, options OrganizationAuditConfigurationOptions) (*OrganizationAuditConfiguration, error)
25+
}
26+
27+
// OrganizationAuditConfiguration represents the auditing configuration for a HCP Terraform Organization.
28+
type OrganizationAuditConfiguration struct {
29+
AuditTrails *OrganizationAuditConfigAuditTrails `jsonapi:"attr,audit-trails,omitempty"`
30+
HCPAuditLogStreaming *OrganizationAuditConfigAuditStreaming `jsonapi:"attr,hcp-audit-log-streaming,omitempty"`
31+
ID string `jsonapi:"primary,audit-configurations"`
32+
Permissions *OrganizationAuditConfigPermissions `jsonapi:"attr,permissions,omitempty"`
33+
Timestamps *OrganizationAuditConfigTimestamps `jsonapi:"attr,timestamps,omitempty"`
34+
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
35+
36+
Organization *Organization `jsonapi:"relation,organization"`
37+
}
38+
39+
type OrganizationAuditConfigAuditTrails struct {
40+
Enabled bool `jsonapi:"attr,enabled"`
41+
}
42+
43+
type OrganizationAuditConfigAuditStreaming struct {
44+
Enabled bool `jsonapi:"attr,enabled"`
45+
OrganizationID string `jsonapi:"attr,organization-id"`
46+
UseDefaultOrganization bool `jsonapi:"attr,use-default-organization"`
47+
}
48+
49+
type OrganizationAuditConfigPermissions struct {
50+
CanEnableHCPAuditLogStreaming bool `jsonapi:"attr,can-enable-hcp-audit-log-streaming"`
51+
CanSetHCPAuditLogStreamingOrganization bool `jsonapi:"attr,can-set-hcp-audit-log-streaming-organization-id"`
52+
CanUseDefaultAuditLogStreamingOrganization bool `jsonapi:"attr,can-use-default-audit-log-streaming-organization"`
53+
}
54+
55+
type OrganizationAuditConfigTimestamps struct {
56+
AuditTrailsDisabledAt *time.Time `jsonapi:"attr,audit-trails-disabled-at,iso8601,omitempty"`
57+
AuditTrailsEnabledAt *time.Time `jsonapi:"attr,audit-trails-enabled-at,iso8601,omitempty"`
58+
AuditTrailsLastFailure *time.Time `jsonapi:"attr,audit-trails-last-failure,iso8601,omitempty"`
59+
AuditTrailsLastSuccess *time.Time `jsonapi:"attr,audit-trails-last-success,iso8601,omitempty"`
60+
HCPAuditLogStreamingDisabledAt *time.Time `jsonapi:"attr,hcp-audit-log-streaming-disabled-at,iso8601,omitempty"`
61+
HCPAuditLogStreamingEnabledAt *time.Time `jsonapi:"attr,hcp-audit-log-streaming-enabled-at,iso8601,omitempty"`
62+
HCPAuditLogStreamingLastFailure *time.Time `jsonapi:"attr,hcp-audit-log-streaming-last-failure,iso8601,omitempty"`
63+
HCPAuditLogStreamingLastSuccess *time.Time `jsonapi:"attr,hcp-audit-log-streaming-last-success,iso8601,omitempty"`
64+
}
65+
66+
type OrganizationAuditConfigurationTest struct {
67+
RequestID *string `json:"request-id,omitempty"`
68+
}
69+
70+
type OrganizationAuditConfigurationOptions struct {
71+
AuditTrails *OrganizationAuditConfigAuditTrails `jsonapi:"attr,audit-trails,omitempty"`
72+
HCPAuditLogStreaming *OrganizationAuditConfigAuditStreaming `jsonapi:"attr,hcp-audit-log-streaming,omitempty"`
73+
}
74+
75+
type organizationAuditConfigurations struct {
76+
client *Client
77+
}
78+
79+
// Read the audit configuration of an organization by its name.
80+
func (s *organizationAuditConfigurations) Read(ctx context.Context, organization string) (*OrganizationAuditConfiguration, error) {
81+
if !validStringID(&organization) {
82+
return nil, ErrInvalidOrg
83+
}
84+
85+
u := fmt.Sprintf("organizations/%s/audit-configuration", url.PathEscape(organization))
86+
req, err := s.client.NewRequest("GET", u, nil)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
ac := &OrganizationAuditConfiguration{}
92+
err = req.Do(ctx, ac)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
return ac, err
98+
}
99+
100+
// Send a test audit event for an organization by its name.
101+
func (s *organizationAuditConfigurations) Test(ctx context.Context, organization string) (*OrganizationAuditConfigurationTest, error) {
102+
if !validStringID(&organization) {
103+
return nil, ErrInvalidOrg
104+
}
105+
106+
u := fmt.Sprintf("organizations/%s/audit-configuration/test", url.PathEscape(organization))
107+
req, err := s.client.NewRequest("POST", u, nil)
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
result := &OrganizationAuditConfigurationTest{}
113+
err = req.DoJSON(ctx, result)
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
return result, err
119+
}
120+
121+
// Update the audit configuration of an organization by its name.
122+
func (s *organizationAuditConfigurations) Update(ctx context.Context, organization string, options OrganizationAuditConfigurationOptions) (*OrganizationAuditConfiguration, error) {
123+
if !validStringID(&organization) {
124+
return nil, ErrInvalidOrg
125+
}
126+
127+
u := fmt.Sprintf("organizations/%s/audit-configuration", url.PathEscape(organization))
128+
req, err := s.client.NewRequest("PATCH", u, &options)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
ac := &OrganizationAuditConfiguration{}
134+
err = req.Do(ctx, ac)
135+
if err != nil {
136+
return nil, err
137+
}
138+
139+
return ac, err
140+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestOrganizationAuditConfigurationRead(t *testing.T) {
15+
skipUnlessBeta(t)
16+
17+
client := testClient(t)
18+
ctx := context.Background()
19+
20+
orgTest, orgTestCleanup := createOrganization(t, client)
21+
defer orgTestCleanup()
22+
23+
newSubscriptionUpdater(orgTest).WithBusinessPlan().Update(t)
24+
25+
if v, err := hasAuditLogging(client, orgTest.Name); err != nil {
26+
t.Fatalf("Could not retrieve the entitlements for the test organization.: %s", err)
27+
} else if !v {
28+
t.Fatal("The test organization requires the audit-logging entitlement but is not entitled.")
29+
return
30+
}
31+
32+
ac, err := client.OrganizationAuditConfigurations.Read(ctx, orgTest.Name)
33+
require.NoError(t, err)
34+
35+
// By default audit trails is enabled
36+
assert.Equal(t, ac.AuditTrails.Enabled, true)
37+
assert.NotNil(t, ac.Organization)
38+
assert.Equal(t, orgTest.Name, ac.Organization.Name)
39+
}
40+
41+
func TestOrganizationAuditConfigurationTest(t *testing.T) {
42+
skipUnlessBeta(t)
43+
44+
client := testClient(t)
45+
ctx := context.Background()
46+
47+
orgTest, orgTestCleanup := createOrganization(t, client)
48+
defer orgTestCleanup()
49+
50+
newSubscriptionUpdater(orgTest).WithBusinessPlan().Update(t)
51+
52+
if v, err := hasAuditLogging(client, orgTest.Name); err != nil {
53+
t.Fatalf("Could not retrieve the entitlements for the test organization.: %s", err)
54+
} else if !v {
55+
t.Fatal("The test organization requires the audit-logging entitlement but is not entitled.")
56+
return
57+
}
58+
59+
result, err := client.OrganizationAuditConfigurations.Test(ctx, orgTest.Name)
60+
require.NoError(t, err)
61+
62+
// Expect a Request ID is returned
63+
assert.NotNil(t, result.RequestID)
64+
}
65+
66+
func TestOrganizationAuditConfigurationUpdate(t *testing.T) {
67+
skipUnlessBeta(t)
68+
69+
client := testClient(t)
70+
ctx := context.Background()
71+
72+
orgTest, orgTestCleanup := createOrganization(t, client)
73+
defer orgTestCleanup()
74+
75+
newSubscriptionUpdater(orgTest).WithBusinessPlan().Update(t)
76+
77+
if v, err := hasAuditLogging(client, orgTest.Name); err != nil {
78+
t.Fatalf("Could not retrieve the entitlements for the test organization.: %s", err)
79+
} else if !v {
80+
t.Fatal("The test organization requires the audit-logging entitlement but is not entitled.")
81+
return
82+
}
83+
84+
ac, err := client.OrganizationAuditConfigurations.Read(ctx, orgTest.Name)
85+
require.NoError(t, err)
86+
87+
// Unfortunately we can't really test the HCP Log Streaming because it requires either an integrated HCP organization,
88+
// or a valid HCP login session. Neither of which are setup for the test organization. Instead we just "update" the settings
89+
// with the existing ones. This doesn't prove that the endpoint behaves properly, but just tests that we can at least send
90+
// a payload to the expected API route.
91+
newCfg, err := client.OrganizationAuditConfigurations.Update(ctx, orgTest.Name, OrganizationAuditConfigurationOptions{
92+
AuditTrails: &OrganizationAuditConfigAuditTrails{
93+
Enabled: ac.AuditTrails.Enabled,
94+
},
95+
HCPAuditLogStreaming: &OrganizationAuditConfigAuditStreaming{
96+
Enabled: ac.HCPAuditLogStreaming.Enabled,
97+
},
98+
})
99+
100+
require.NoError(t, err)
101+
102+
assert.Equal(t, ac.AuditTrails.Enabled, newCfg.AuditTrails.Enabled)
103+
assert.Equal(t, ac.HCPAuditLogStreaming.Enabled, newCfg.HCPAuditLogStreaming.Enabled)
104+
}

run_task_integration_test.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,13 @@ package tfe
55

66
import (
77
"context"
8-
"errors"
98
"os"
109
"testing"
1110

1211
"github.com/stretchr/testify/assert"
1312
"github.com/stretchr/testify/require"
1413
)
1514

16-
func getOrgEntitlements(client *Client, organizationName string) (*Entitlements, error) {
17-
ctx := context.Background()
18-
orgEntitlements, err := client.Organizations.ReadEntitlements(ctx, organizationName)
19-
if err != nil {
20-
return nil, err
21-
}
22-
if orgEntitlements == nil {
23-
return nil, errors.New("The organization entitlements are empty.")
24-
}
25-
return orgEntitlements, nil
26-
}
27-
28-
func hasGlobalRunTasks(client *Client, organizationName string) (bool, error) {
29-
oe, err := getOrgEntitlements(client, organizationName)
30-
if err != nil {
31-
return false, err
32-
}
33-
return oe.GlobalRunTasks, nil
34-
}
35-
36-
func hasPrivateRunTasks(client *Client, organizationName string) (bool, error) {
37-
oe, err := getOrgEntitlements(client, organizationName)
38-
if err != nil {
39-
return false, err
40-
}
41-
return oe.PrivateRunTasks, nil
42-
}
43-
4415
func TestRunTasksCreate(t *testing.T) {
4516
client := testClient(t)
4617
ctx := context.Background()

0 commit comments

Comments
 (0)