Skip to content

Commit

Permalink
feat: implement GET /auth_logs/ and /auth_logs/{id}
Browse files Browse the repository at this point in the history
Signed-off-by: James Ramirez <[email protected]>
  • Loading branch information
ramirezj committed Aug 5, 2024
1 parent 6c8d8f1 commit 6fc96ed
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ using the interactive API documentation at https://kolideapi.readme.io/reference
| GET | /people/{id} | :question: | ? | ? | Ok |
| GET | /person_groups | :white_check_mark: | ? | ? | [^1] |
| GET | /person_groups/{id} | :white_check_mark: | ? | ? | [^1] |
| GET | /auth_logs | #27 | ? | ? | |
| GET | /auth_logs/{id} | #28 | ? | ? | |
| GET | /auth_logs | :question: | ? | ? | |
| GET | /auth_logs/{id} | :question: | ? | ? | |
| GET | /device_groups/{deviceGroupId}/devices | :white_check_mark: | ? | ? | [^1] |
| POST | /device_groups/{deviceGroupId}/memberships | :no_entry_sign: | | | |
| DELETE | /device_groups/{deviceGroupId}/memberships/{id} | :no_entry_sign: | | | |
Expand Down
46 changes: 46 additions & 0 deletions docs/tables/kolide_auth_log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Table: kolide_auth_log

Lists the authentication attempts occurring when a user tries to sign into an App protected by Kolide Device Trust.

## Examples

### Basic info

```sql
select
timestamp,
person_name,
initial_status,
result
from
kolide_auth_log;
```

### List all attempts from the past day

```sql
select
timestamp,
person_name,
initial_status,
result
from
kolide_auth_log
where
timestamp > date_trunc('day', current_date) - interval '1 day';
```

### List all failed attempts performed by a specific user

```sql
select
timestamp,
initial_status,
result
from
kolide_auth_log
where
person_name = 'Dennis Nedry'
and
result = 'Fail';
```
52 changes: 52 additions & 0 deletions kolide/client/auth_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package kolide_client

import (
"time"
)

type AuthLogListResponse struct {
AuthLogs []AuthLog `json:"data"`
Pagination Pagination `json:"pagination"`
}
type AuthLog struct {
Id string `json:"id"`
Timestamp time.Time `json:"timestamp"`
PersonName string `json:"person_name"`
PersonEmail string `json:"person_email"`
PersonInfo PersonInformation `json:"person_info"`
DeviceInfo DeviceInformation `json:"device_information,omitempty"`
Result string `json:"result"`
InitialStatus string `json:"initial_status"`
IpAddress string `json:"ip_address"`
AgentVersion string `json:"agent_version,omitempty"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
BrowserName string `json:"browser_name"`
BrowserUserAgent string `json:"browser_user_agent"`
IssuesDisplayed []DetailedIssueInformation `json:"issues_displayed"`
Events []EventInformation `json:"events"`
}

type PersonInformation struct {
Identifier string `json:"identifier,omitempty"`
}

type DetailedIssueInformation struct {
Title string `json:"title,omitempty"`
BlockingStatus string `json:"blocking_status,omitempty"`
Identifier string `json:"identifier,omitempty"`
}

type EventInformation struct {
Timestamp time.Time `json:"timestamp"`
EventType string `json:"event_type,omitempty"`
EventDescription string `json:"event_description,omitempty"`
}

func (c *Client) GetAuthLogs(cursor string, limit int32, searches ...Search) (interface{}, error) {
return c.fetchCollection("/auth_logs/", cursor, limit, searches, new(AuthLogListResponse))
}

func (c *Client) GetAuthLogById(id string) (interface{}, error) {
return c.fetchResource("/auth_logs/", id, new(AuthLog))
}
1 change: 1 addition & 0 deletions kolide/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
TableMap: map[string]*plugin.Table{
"kolide_admin_user": tableKolideAdminUser(ctx),
"kolide_audit_log": tableKolideAuditLog(ctx),
"kolide_auth_log": tableKolideAuthLog(ctx),
"kolide_check": tableKolideCheck(ctx),
"kolide_deprovisioned_person": tableKolideDeprovisionedPerson(ctx),
"kolide_device": tableKolideDevice(ctx),
Expand Down
77 changes: 77 additions & 0 deletions kolide/table_kolide_auth_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package kolide

import (
"context"

kolide "github.com/grendel-consulting/steampipe-plugin-kolide/kolide/client"
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
)

//// TABLE DEFINITION

func tableKolideAuthLog(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "kolide_auth_log",
Description: "Authentication events occurring when a user tries to sign into an App protected by Kolide Device Trust.",
Columns: []*plugin.Column{
// Filterable "top" columns
{Name: "id", Description: "Canonical identifier for this auth event.", Type: proto.ColumnType_STRING},
{Name: "timestamp", Description: "When this event started.", Type: proto.ColumnType_TIMESTAMP},
{Name: "ip_address", Description: "IP address of the request intiating this auth event, may be IPv4 or IPv6.", Type: proto.ColumnType_STRING},
{Name: "agent_version", Description: "Version of the Kolide Agent running on the endpoint, if known.", Type: proto.ColumnType_STRING},
{Name: "country", Description: "Name of the country that the session originated from, determined by IP addres and subject to the limitations of IP geocoding.", Type: proto.ColumnType_STRING},
{Name: "city", Description: "Name of the city that the session originated from, determined by IP addres and subject to the limitations of IP geocoding.", Type: proto.ColumnType_STRING},
{Name: "browser_name", Description: "Common name of the browser used to initiate the session, subject to the limitations and accuracy of browser detection.", Type: proto.ColumnType_STRING},
// Other columns
{Name: "person_name", Description: "Name of the user triggering this auth event.", Type: proto.ColumnType_STRING},
{Name: "person_email", Description: "Email of the user triggering this auth event.", Type: proto.ColumnType_STRING},
{Name: "person_id", Description: "Canonical identifier for the user this auth event relates to.", Type: proto.ColumnType_STRING, Transform: transform.FromField("PersonInformation.Identifier")},
{Name: "device_id", Description: "Canonical identifier for the device this auth event relates to.", Type: proto.ColumnType_STRING, Transform: transform.FromField("DeviceInformation.Identifier")},
{Name: "result", Description: "Result of the authentication attempt, either Success or Fail.", Type: proto.ColumnType_STRING},
{Name: "initial_status", Description: "Initial auth status of the device attempting authentication, one of All_Good, Will_Block, Blocked or Unknown if no device was detected.", Type: proto.ColumnType_STRING},
{Name: "browser_user_agent", Description: "User agent information for the browser used to initiatie this session, subject to the limitations and accuracy of browser detection.", Type: proto.ColumnType_STRING},
{Name: "issues_displayed", Description: "List of issue titles and blocking status that were displayed to the end user", Type: proto.ColumnType_JSON, Transform: transform.FromField("IssuesDisplayed")},
{Name: "events", Description: "Events that occured during this authentication session", Type: proto.ColumnType_JSON, Transform: transform.FromField("Events")},
// Steampipe standard columns
{Name: "title", Description: "Display name for this event.", Type: proto.ColumnType_STRING, Transform: transform.FromField("Result")},
},
List: &plugin.ListConfig{
KeyColumns: []*plugin.KeyColumn{
// Using Kolide API query feature, can be combined with AND (and OR)
{Name: "timestamp", Require: plugin.Optional, Operators: []string{"=", ">", "<"}},
{Name: "city", Require: plugin.Optional, Operators: []string{"=", "~~"}},
{Name: "country", Require: plugin.Optional, Operators: []string{"=", "~~"}},
{Name: "ip_address", Require: plugin.Optional, Operators: []string{"=", "~~"}},
{Name: "agent_version", Require: plugin.Optional, Operators: []string{"=", "~~"}},
{Name: "browser_name", Require: plugin.Optional, Operators: []string{"=", "~~"}},
},
Hydrate: listAuthLogs,
},
Get: &plugin.GetConfig{
KeyColumns: plugin.SingleColumn("id"),
Hydrate: getAuthLog,
},
}
}

//// LIST FUNCTION

func listAuthLogs(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
var visitor ListPredicate = func(client *kolide.Client, cursor string, limit int32, searches ...kolide.Search) (interface{}, error) {
return client.GetAuthLogs(cursor, limit, searches...)
}

return listAnything(ctx, d, h, "kolide_auth_log.listAuthLogs", visitor, "AuthLogs")
}

//// GET FUNCTION

func getAuthLog(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
var visitor GetPredicate = func(client *kolide.Client, id string) (interface{}, error) {
return client.GetAuthLogById(id)
}

return getAnything(ctx, d, h, "kolide_auth_log.getAuthLog", "id", visitor)
}
9 changes: 9 additions & 0 deletions test/end-to-end/_query/kolide_auth_log.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
select
timestamp,
person_name,
initial_status,
result
from
kolide_auth_log
order by
timestamp asc;
9 changes: 9 additions & 0 deletions test/end-to-end/_query/kolide_auth_log_by_id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
select
timestamp,
person_name,
initial_status,
result
from
kolide_auth_log
where
id = '1234';
9 changes: 9 additions & 0 deletions test/end-to-end/_results/kolide_auth_log.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

define_test_results(){
export EXPECTED_COUNT="0"
# export ID=""
# export PERSON_NAME=""
# export INITIAL_STATUS=""
# export RESULT=""
}
59 changes: 59 additions & 0 deletions test/end-to-end/kolide_auth_log.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# bats file_tags=table:kolide_auth_log, output:auth_log

setup_file() {
load "${BATS_TEST_DIRNAME}/_support/globals.bash"
define_file_globals

define_common_test_results

if [[ -f $EXPECTED_RESULTS ]]; then
load $EXPECTED_RESULTS
fi
}

setup() {
load "${BATS_TEST_DIRNAME}/_support/extensions.bash"
load_helpers
}

#bats test_tags=scope:smoke
@test "can_execute_query_via_steampipe" {
steampipe query $QUERY_UNDER_TEST --output json > $QUERY_RESULTS
assert_exists $QUERY_RESULTS
}

@test "has_expected_number_of_results" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows | length'"

if [[ -z "$EXPECTED_COUNT" ]]; then assert_output $EXPECTED_COUNT ; else assert [ "$output" -ge "1" ] ; fi
}

#bats test_tags=exactness:fuzzy
@test "has_expected_timestamp" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows.[0].timestamp'"
if [[ -z "$TIMESTAMP" ]]; then assert_output --partial $TIMESTAMP ; else assert_success ; fi
}

#bats test_tags=exactness:fuzzy
@test "has_expected_person_name" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows.[0].person_name'"
if [[ -z "$PERSON_NAME" ]]; then assert_output --partial $PERSON_NAME ; else assert_success ; fi
}

#bats test_tags=exactness:default
@test "has_expected_initial_status" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows.[0].initial_status'"
if [[ -z "$INITIAL_STATUS" ]]; then assert_output $INITIAL_STATUS ; else assert_output "full" ; fi
}

#bats test_tags=exactness:default
@test "has_expected_result" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows.[0].result'"
if [[ -z "$RESULT" ]]; then assert_output $RESULT ; else assert_output "full" ; fi
}

teardown_file(){
if [[ -f $QUERY_RESULTS ]]; then
rm -f $QUERY_RESULTS
fi
}
30 changes: 30 additions & 0 deletions test/end-to-end/kolide_auth_log_by_id.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# bats file_tags=table:kolide_auth_log, output:auth_log

setup_file() {
load "${BATS_TEST_DIRNAME}/_support/globals.bash"
define_file_globals
}

setup() {
load "${BATS_TEST_DIRNAME}/_support/extensions.bash"
load_helpers
}

#bats test_tags=scope:smoke
@test "can_execute_query_via_steampipe" {
steampipe query $QUERY_UNDER_TEST --output json > $QUERY_RESULTS
assert_exists $QUERY_RESULTS
}

@test "has_no_more_than_one_result" {
run bash -c "cat $QUERY_RESULTS | jq -r '.rows | length'"
assert [ "$output" -le "1" ]
}

# Remaining functionality covered in kolide_auth_log.bats

teardown_file(){
if [[ -f $QUERY_RESULTS ]]; then
rm -f $QUERY_RESULTS
fi
}

0 comments on commit 6fc96ed

Please sign in to comment.