Skip to content

Commit

Permalink
Changes to the configuration defaults, the poll interval type and sup…
Browse files Browse the repository at this point in the history
…ported runtimes"
  • Loading branch information
rnishtala-sumo committed Oct 24, 2023
1 parent 3ed46ba commit 57acf39
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 30 deletions.
10 changes: 6 additions & 4 deletions pkg/receiver/activedirectoryinvreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Stability level**: Alpha

A Windows Active Directory Inventory receiver collects inventory data from Active Directory. This includes information such as computer names, user names, email addresses, and location information
A Windows Active Directory Inventory receiver collects inventory data from Active Directory. This includes information such as computer names, user names, email addresses, and location information. This receiver is only supported on Windows servers.

Supported pipeline types: logs

Expand All @@ -12,14 +12,16 @@ Supported pipeline types: logs
receivers:
activedirectoryinv:
# Base DN
# default = ""
base_dn: "CN=Users,DC=exampledomain,DC=com"

# User attributes
# default = [name, mail, department, manager, memberOf]
attributes: [name, mail, department, manager, memberOf]

# The polling interval.
# default = 60
poll_interval: 60
# default = 24h
poll_interval: 24h
```
The full list of settings exposed for this receiver are documented in
Expand All @@ -33,7 +35,7 @@ receivers:
activedirectoryinv:
base_dn: "CN=Users,DC=exampledomain,DC=com"
attributes: [name, mail, department, manager, memberOf]
poll_interval: 60
poll_interval: 24h

exporters:
logging:
Expand Down
19 changes: 15 additions & 4 deletions pkg/receiver/activedirectoryinvreceiver/adinvreceiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,20 @@ type ADReceiver struct {
config *ADConfig
logger *zap.Logger
client Client
runtime RuntimeInfo
consumer consumer.Logs
wg *sync.WaitGroup
doneChan chan bool
}

// newLogsReceiver creates a new Active Directory Inventory receiver
func newLogsReceiver(cfg *ADConfig, logger *zap.Logger, client Client, consumer consumer.Logs) *ADReceiver {
func newLogsReceiver(cfg *ADConfig, logger *zap.Logger, client Client, runtime RuntimeInfo, consumer consumer.Logs) *ADReceiver {

return &ADReceiver{
config: cfg,
logger: logger,
client: client,
runtime: runtime,
consumer: consumer,
wg: &sync.WaitGroup{},
doneChan: make(chan bool),
Expand All @@ -79,6 +81,9 @@ func newLogsReceiver(cfg *ADConfig, logger *zap.Logger, client Client, consumer

// Start the logs receiver
func (l *ADReceiver) Start(ctx context.Context, _ component.Host) error {
if !l.runtime.SupportedOS() {
return errSupportedOS
}
l.logger.Debug("Starting to poll for active directory inventory records")
l.wg.Add(1)
go l.startPolling(ctx)
Expand All @@ -96,7 +101,13 @@ func (l *ADReceiver) Shutdown(_ context.Context) error {
// Start polling for Active Directory inventory records
func (l *ADReceiver) startPolling(ctx context.Context) {
defer l.wg.Done()
t := time.NewTicker(l.config.PollInterval * time.Second)
duration, err := time.ParseDuration(l.config.PollInterval)
if err != nil {
l.logger.Error("Failed to parse poll interval", zap.Error(err))
return
}
l.logger.Info("Polling interval: ", zap.Duration("interval", duration))
t := time.NewTicker(duration)
for {
select {
case <-ctx.Done():
Expand Down Expand Up @@ -145,10 +156,10 @@ func (r *ADReceiver) poll(ctx context.Context) error {
resourceLogs := &rl
_ = resourceLogs.ScopeLogs().AppendEmpty()
root, err := r.client.Open(r.config.DN, resourceLogs)
r.traverse(root, r.config.Attributes, resourceLogs)
if err != nil {
return err
return fmt.Errorf("Invalid Distinguished Name, please verify that the domain exists: %w", err)
}
r.traverse(root, r.config.Attributes, resourceLogs)
err = r.consumer.ConsumeLogs(ctx, logs)
if err != nil {
r.logger.Error("Error consuming log", zap.Error(err))
Expand Down
37 changes: 32 additions & 5 deletions pkg/receiver/activedirectoryinvreceiver/adinvreceiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ import (
"go.uber.org/zap"
)

type MockRuntime struct {
mock.Mock
}

func (mr *MockRuntime) SupportedOS() bool {
args := mr.Called()
return args.Bool(0)
}

type MockClient struct {
mock.Mock
}
Expand Down Expand Up @@ -89,8 +98,10 @@ func TestStart(t *testing.T) {

sink := &consumertest.LogsSink{}
mockClient := &MockClient{}
mockRuntime := &MockRuntime{}
mockRuntime.On("SupportedOS").Return(true)

logsRcvr := newLogsReceiver(cfg, zap.NewNop(), mockClient, sink)
logsRcvr := newLogsReceiver(cfg, zap.NewNop(), mockClient, mockRuntime, sink)

err := logsRcvr.Start(context.Background(), componenttest.NewNopHost())
require.NoError(t, err)
Expand All @@ -99,16 +110,32 @@ func TestStart(t *testing.T) {
require.NoError(t, err)
}

func TestStartUnsupportedOS(t *testing.T) {
cfg := CreateDefaultConfig().(*ADConfig)
cfg.DN = "CN=Guest,CN=Users,DC=exampledomain,DC=com"

sink := &consumertest.LogsSink{}
mockClient := &MockClient{}
mockRuntime := &MockRuntime{}
mockRuntime.On("SupportedOS").Return(false)

logsRcvr := newLogsReceiver(cfg, zap.NewNop(), mockClient, mockRuntime, sink)

err := logsRcvr.Start(context.Background(), componenttest.NewNopHost())
require.Error(t, err)
require.Contains(t, err.Error(), "activedirectoryinv is only supported on Windows")
}

func TestPoll(t *testing.T) {
cfg := CreateDefaultConfig().(*ADConfig)
cfg.DN = "CN=Guest,CN=Users,DC=exampledomain,DC=com"
cfg.PollInterval = 1
cfg.PollInterval = "1s"
cfg.Attributes = []string{"name"}

sink := &consumertest.LogsSink{}
mockClient := defaultMockClient()

logsRcvr := newLogsReceiver(cfg, zap.NewNop(), mockClient, sink)
mockRuntime := &MockRuntime{}
mockRuntime.On("SupportedOS").Return(true)
logsRcvr := newLogsReceiver(cfg, zap.NewNop(), mockClient, mockRuntime, sink)

err := logsRcvr.Start(context.Background(), componenttest.NewNopHost())
require.NoError(t, err)
Expand Down
16 changes: 16 additions & 0 deletions pkg/receiver/activedirectoryinvreceiver/adwrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package activedirectoryinvreceiver

import (
"runtime"

adsi "github.com/go-adsi/adsi"
)

Expand Down Expand Up @@ -69,6 +71,7 @@ func (o *ADObject) Attrs(key string) ([]interface{}, error) {
return o.windowsADObject.Attr(key)
}

// ToContainer converts an Active Directory object to an Active Directory container
func (o *ADObject) ToContainer() (Container, error) {
container, err := o.windowsADObject.ToContainer()
if err != nil {
Expand Down Expand Up @@ -97,3 +100,16 @@ func (o *ADObjectIter) Next() (*adsi.Object, error) {
func (o *ADObjectIter) Close() {
o.windowsADObjectIter.Close()
}

// RuntimeInfo is an interface for runtime information
type RuntimeInfo interface {
SupportedOS() bool
}

// ADRuntimeInfo is a wrapper for runtime information
type ADRuntimeInfo struct{}

// SupportedOS returns whether the runtime is supported
func (r *ADRuntimeInfo) SupportedOS() bool {
return (runtime.GOOS == "windows")
}
20 changes: 13 additions & 7 deletions pkg/receiver/activedirectoryinvreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,27 @@ import (
// ADConfig defines configuration for Active Directory Inventory receiver.

type ADConfig struct {
DN string `mapstructure:"base_dn"` // DN is the base distinguished name to search from
Attributes []string `mapstructure:"attributes"`
PollInterval time.Duration `mapstructure:"poll_interval"`
DN string `mapstructure:"base_dn"` // DN is the base distinguished name to search from
Attributes []string `mapstructure:"attributes"`
PollInterval string `mapstructure:"poll_interval"`
}

var (
errInvalidDN = errors.New("Base DN is incorrect, it must be in the format of CN=Users,DC=exampledomain,DC=com")
errInvalidPollInterval = errors.New("poll interval is incorrect, it must be a duration greater than one second")
errInvalidDN = errors.New("Base DN is incorrect, it must be a valid distinguished name (CN=Guest,OU=Users,DC=example,DC=com)")
errInvalidPollInterval = errors.New("poll interval is incorrect, invalid duration")
errSupportedOS = errors.New(typeStr + " is only supported on Windows.")
)

func isValidDuration(durationStr string) bool {
_, err := time.ParseDuration(durationStr)
return err == nil
}

// Validate validates all portions of the relevant config
func (c *ADConfig) Validate() error {

// Define the regular expression pattern for a valid Base DN
pattern := `^((CN|OU)=[^,]+(,|$))*((DC=[^,]+),?)+$`
pattern := `^((CN|OU)=[^,]+(,|$))*((DC=[^,]+),?)*$`

// Compile the regular expression pattern
regex := regexp.MustCompile(pattern)
Expand All @@ -47,7 +53,7 @@ func (c *ADConfig) Validate() error {
return errInvalidDN
}

if c.PollInterval < 0 {
if !isValidDuration(c.PollInterval) {
return errInvalidPollInterval
}

Expand Down
22 changes: 16 additions & 6 deletions pkg/receiver/activedirectoryinvreceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,48 @@ func TestValidate(t *testing.T) {
config: ADConfig{
DN: "CN=Guest,CN=Users,DC=exampledomain,DC=com",
Attributes: []string{"name"},
PollInterval: 60,
PollInterval: "60s",
},
},
{
name: "Valid Config DC",
config: ADConfig{
DN: "DC=exampledomain,DC=com",
Attributes: []string{"name"},
PollInterval: 60,
PollInterval: "60s",
},
},
{
name: "Valid Config OU",
config: ADConfig{
DN: "CN=Guest,OU=Users,DC=exampledomain,DC=com",
Attributes: []string{"name"},
PollInterval: 60,
PollInterval: "24h",
},
},
{
name: "Invalid No CN",
name: "Invalid DN",
config: ADConfig{
DN: "",
DN: "NA",
Attributes: []string{"name"},
PollInterval: "24h",
},
expectedErr: errInvalidDN,
},
{
name: "Empty DN",
config: ADConfig{
DN: "",
Attributes: []string{"name"},
PollInterval: "24h",
},
},
{
name: "Invalid Poll Interval",
config: ADConfig{
DN: "CN=Users,DC=exampledomain,DC=com",
Attributes: []string{"name"},
PollInterval: -1,
PollInterval: "test",
},
expectedErr: errInvalidPollInterval,
},
Expand Down
9 changes: 5 additions & 4 deletions pkg/receiver/activedirectoryinvreceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ func NewFactory() receiver.Factory {
// CreateDefaultConfig creates the default configuration for the receiver
func CreateDefaultConfig() component.Config {
return &ADConfig{
DN: "CN=Guest,CN=Users,DC=exampledomain,DC=com",
Attributes: []string{"name"},
PollInterval: 60,
DN: "",
Attributes: []string{"name", "mail", "department", "manager", "memberOf"},
PollInterval: "24h",
}
}

Expand All @@ -53,6 +53,7 @@ func createLogsReceiver(
) (receiver.Logs, error) {
cfg := rConf.(*ADConfig)
adsiClient := &ADSIClient{}
rcvr := newLogsReceiver(cfg, params.Logger, adsiClient, consumer)
adRuntime := &ADRuntimeInfo{}
rcvr := newLogsReceiver(cfg, params.Logger, adsiClient, adRuntime, consumer)
return rcvr, nil
}

0 comments on commit 57acf39

Please sign in to comment.