mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Refactor teams service methods (#910)
- Move team-related service methods to `ee/server/service`. - Instantiate different service on startup based on license key. - Refactor service errors into separate package. - Add support for running E2E tests in both Core and Basic tiers.
This commit is contained in:
parent
9876dbe6b6
commit
417ef2c9b6
61 changed files with 813 additions and 694 deletions
5
.github/workflows/test.yml
vendored
5
.github/workflows/test.yml
vendored
|
|
@ -6,6 +6,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
go-version: ['^1.16.0']
|
||||
fleet-tier: [core, basic]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
|
|
@ -68,10 +69,10 @@ jobs:
|
|||
- name: Run E2E Tests
|
||||
run: |
|
||||
make e2e-reset-db
|
||||
make e2e-serve &
|
||||
make e2e-serve-${{ matrix.fleet-tier }} &
|
||||
sleep 3
|
||||
make e2e-setup
|
||||
yarn cypress run --config video=false
|
||||
CYPRESS_FLEET_TIER=${{ matrix.fleet-tier }} yarn cypress run --config video=false
|
||||
|
||||
|
||||
test-js:
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -229,5 +229,9 @@ e2e-setup:
|
|||
./build/fleetctl user create --context e2e --email=test+1@example.com --username=test1 --password=admin123#
|
||||
./build/fleetctl user create --context e2e --email=test+2@example.com --username=test2 --password=admin123#
|
||||
|
||||
e2e-serve:
|
||||
./build/fleet serve --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e --server_address=localhost:8642
|
||||
e2e-serve-core:
|
||||
./build/fleet serve --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e --server_address=localhost:8642
|
||||
|
||||
e2e-serve-basic:
|
||||
./build/fleet serve --dev_license --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e --server_address=localhost:8642
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import (
|
|||
|
||||
"github.com/WatchBeam/clock"
|
||||
"github.com/e-dard/netbug"
|
||||
"github.com/fleetdm/fleet/ee/licensing"
|
||||
"github.com/fleetdm/fleet/ee/server/licensing"
|
||||
eeservice "github.com/fleetdm/fleet/ee/server/service"
|
||||
"github.com/fleetdm/fleet/server/config"
|
||||
"github.com/fleetdm/fleet/server/datastore/mysql"
|
||||
"github.com/fleetdm/fleet/server/datastore/s3"
|
||||
|
|
@ -53,6 +54,8 @@ func createServeCmd(configManager config.Manager) *cobra.Command {
|
|||
debug := false
|
||||
// Whether to enable developer options
|
||||
dev := false
|
||||
// Whether to enable development Fleet Basic license
|
||||
devLicense := false
|
||||
|
||||
serveCmd := &cobra.Command{
|
||||
Use: "serve",
|
||||
|
|
@ -72,6 +75,11 @@ the way that the Fleet server works.
|
|||
applyDevFlags(&config)
|
||||
}
|
||||
|
||||
if devLicense {
|
||||
// This license key is valid for development only
|
||||
config.License.Key = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGbGVldCBEZXZpY2UgTWFuYWdlbWVudCBJbmMuIiwiZXhwIjoxNjQwOTk1MjAwLCJzdWIiOiJkZXZlbG9wbWVudCIsImRldmljZXMiOjEwMCwibm90ZSI6ImZvciBkZXZlbG9wbWVudCBvbmx5IiwidGllciI6ImJhc2ljIiwiaWF0IjoxNjIyNDI2NTg2fQ.WmZ0kG4seW3IrNvULCHUPBSfFdqj38A_eiXdV_DFunMHechjHbkwtfkf1J6JQJoDyqn8raXpgbdhafDwv3rmDw"
|
||||
}
|
||||
|
||||
license, err := licensing.LoadLicense(config.License.Key)
|
||||
if err != nil {
|
||||
initFatal(
|
||||
|
|
@ -233,6 +241,13 @@ the way that the Fleet server works.
|
|||
initFatal(err, "initializing service")
|
||||
}
|
||||
|
||||
if license.Tier == kolide.TierBasic {
|
||||
svc, err = eeservice.NewService(svc, ds, logger, config, mailService, clock.C, license)
|
||||
if err != nil {
|
||||
initFatal(err, "initial Fleet Basic service")
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
for {
|
||||
|
|
@ -409,6 +424,7 @@ the way that the Fleet server works.
|
|||
|
||||
serveCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug endpoints")
|
||||
serveCmd.PersistentFlags().BoolVar(&dev, "dev", false, "Enable developer options")
|
||||
serveCmd.PersistentFlags().BoolVar(&devLicense, "dev_license", false, "Enable development license")
|
||||
|
||||
return serveCmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"baseUrl": "https://localhost:8642",
|
||||
"fixturesFolder": false
|
||||
"fixturesFolder": false,
|
||||
"testFiles": "**/*.ts"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
describe("Teams flow", () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it("Create, edit, and delete a team successfully", () => {
|
||||
cy.visit("/settings/teams");
|
||||
|
||||
cy.findByRole("button", { name: /create team/i }).click();
|
||||
|
||||
cy.findByLabelText(/team name/i)
|
||||
.click()
|
||||
.type("Valor");
|
||||
|
||||
// ^$ forces exact match
|
||||
cy.findByRole("button", { name: /^create$/i }).click();
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.contains("Valor").click({ force: true });
|
||||
|
||||
cy.findByText(/agent options/i).click();
|
||||
|
||||
cy.get(".ace_content")
|
||||
.click()
|
||||
.type("{selectall}{backspace}apiVersion: v1{enter}kind: options");
|
||||
|
||||
cy.findByRole("button", { name: /save/i }).click();
|
||||
|
||||
cy.visit("/settings/teams/1/options");
|
||||
|
||||
cy.contains(/apiVersion: v1/i).should("be.visible");
|
||||
cy.contains(/kind: options/i).should("be.visible");
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
|
||||
cy.contains("Valor").get(".Select-arrow-zone").click();
|
||||
|
||||
// need force:true for dropdown
|
||||
cy.findByText(/edit/i).click({ force: true });
|
||||
|
||||
cy.findByLabelText(/team name/i)
|
||||
.click()
|
||||
.type("{selectall}{backspace}Mystic");
|
||||
|
||||
cy.findByRole("button", { name: /save/i }).click();
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.contains("Mystic").get(".Select-arrow-zone").click();
|
||||
|
||||
cy.findByText(/delete/i).click({ force: true });
|
||||
|
||||
cy.findByRole("button", { name: /delete/i }).click();
|
||||
|
||||
cy.findByText(/successfully deleted/i).should("be.visible");
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.findByText(/mystic/i).should("not.exist");
|
||||
});
|
||||
});
|
||||
23
cypress/integration/basic/README.md
Normal file
23
cypress/integration/basic/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Basic tier tests
|
||||
|
||||
These tests should only run when the server is in `basic` tier.
|
||||
|
||||
To enable the tests:
|
||||
|
||||
```sh
|
||||
export CYPRESS_FLEET_TIER=basic
|
||||
```
|
||||
|
||||
Before running the appropriate `yarn cypress (open|run)` command.
|
||||
|
||||
## Filtering
|
||||
|
||||
Any test suite in this directory should use the following pattern for filtering:
|
||||
|
||||
**FIXME**: There must be a better way to do this for all tests in the directory rather than having to add the check in each file?
|
||||
|
||||
```js
|
||||
if (Cypress.env("FLEET_TIER") === "basic") {
|
||||
// test suite here
|
||||
}
|
||||
```
|
||||
74
cypress/integration/basic/teamflow.spec.ts
Normal file
74
cypress/integration/basic/teamflow.spec.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
if (Cypress.env("FLEET_TIER") === "basic") {
|
||||
describe("Teams flow", () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it("Create, edit, and delete a team successfully", () => {
|
||||
cy.visit("/settings/teams");
|
||||
|
||||
cy.findByRole("button", { name: /create team/i }).click();
|
||||
|
||||
cy.findByLabelText(/team name/i)
|
||||
.click()
|
||||
.type("Valor");
|
||||
|
||||
// ^$ forces exact match
|
||||
cy.findByRole("button", { name: /^create$/i }).click();
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.contains("Valor").click({ force: true });
|
||||
|
||||
cy.findByText(/agent options/i).click();
|
||||
|
||||
cy.get(".ace_content")
|
||||
.click()
|
||||
.type("{selectall}{backspace}apiVersion: v1{enter}kind: options");
|
||||
|
||||
cy.findByRole("button", { name: /save/i }).click();
|
||||
|
||||
cy.visit("/settings/teams/1/options");
|
||||
|
||||
cy.contains(/apiVersion: v1/i).should("be.visible");
|
||||
cy.contains(/kind: options/i).should("be.visible");
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
|
||||
cy.contains("Valor").get(".Select-arrow-zone").click();
|
||||
|
||||
// need force:true for dropdown
|
||||
cy.findByText(/edit/i).click({ force: true });
|
||||
|
||||
cy.findByLabelText(/team name/i)
|
||||
.click()
|
||||
.type("{selectall}{backspace}Mystic");
|
||||
|
||||
cy.findByRole("button", { name: /save/i }).click();
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.contains("Mystic").get(".Select-arrow-zone").click();
|
||||
|
||||
cy.findByText(/delete/i).click({ force: true });
|
||||
|
||||
cy.findByRole("button", { name: /delete/i }).click();
|
||||
|
||||
cy.findByText(/successfully deleted/i).should("be.visible");
|
||||
|
||||
cy.visit("/settings/teams");
|
||||
// Allow rendering to settle
|
||||
// TODO this might represent a bug in the React code.
|
||||
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||
|
||||
cy.findByText(/mystic/i).should("not.exist");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -135,9 +135,18 @@ E2E tests are run using Docker and Cypress.
|
|||
|
||||
Make sure dependencies are up to date and the [Fleet binaries are built locally](./1-Building-Fleet.md).
|
||||
|
||||
For Fleet Core tests:
|
||||
|
||||
```
|
||||
make e2e-reset-db
|
||||
make e2e-serve
|
||||
make e2e-serve-core
|
||||
```
|
||||
|
||||
For Fleet Basic tests:
|
||||
|
||||
```
|
||||
make e2e-reset-db
|
||||
make e2e-serve-basic
|
||||
```
|
||||
|
||||
This will start a local Fleet server connected to the E2E database. Leave this server running for the duration of end-to-end testing.
|
||||
|
|
@ -154,16 +163,32 @@ Tests can be run in interactive mode, or from the command line.
|
|||
|
||||
#### Interactive
|
||||
|
||||
For Fleet Core tests:
|
||||
|
||||
```
|
||||
yarn cypress open
|
||||
CYPRESS_FLEET_TIER=core yarn cypress open
|
||||
```
|
||||
|
||||
For Fleet Basic tests:
|
||||
|
||||
```
|
||||
CYPRESS_FLEET_TIER=basic yarn cypress open
|
||||
```
|
||||
|
||||
Use the graphical UI controls to run and view tests.
|
||||
|
||||
#### Command line
|
||||
|
||||
For Fleet Core tests:
|
||||
|
||||
```
|
||||
yarn cypress run
|
||||
CYPRESS_FLEET_TIER=core yarn cypress run
|
||||
```
|
||||
|
||||
For Fleet Basic tests:
|
||||
|
||||
```
|
||||
CYPRESS_FLEET_TIER=basic yarn cypress run
|
||||
```
|
||||
|
||||
Tests will run automatically and results are reported to the shell.
|
||||
|
|
|
|||
37
ee/server/service/service.go
Normal file
37
ee/server/service/service.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/WatchBeam/clock"
|
||||
"github.com/fleetdm/fleet/server/config"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
kolide.Service
|
||||
|
||||
ds kolide.Datastore
|
||||
logger kitlog.Logger
|
||||
config config.KolideConfig
|
||||
clock clock.Clock
|
||||
license *kolide.LicenseInfo
|
||||
}
|
||||
|
||||
func NewService(
|
||||
svc kolide.Service,
|
||||
ds kolide.Datastore,
|
||||
logger kitlog.Logger,
|
||||
config config.KolideConfig,
|
||||
mailService kolide.MailService,
|
||||
c clock.Clock,
|
||||
license *kolide.LicenseInfo,
|
||||
) (*Service, error) {
|
||||
return &Service{
|
||||
Service: svc,
|
||||
ds: ds,
|
||||
logger: logger,
|
||||
config: config,
|
||||
clock: c,
|
||||
license: license,
|
||||
}, nil
|
||||
}
|
||||
160
ee/server/service/service_teams.go
Normal file
160
ee/server/service/service_teams.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/fleetdm/fleet/server/contexts/viewer"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc *Service) NewTeam(ctx context.Context, p kolide.TeamPayload) (*kolide.Team, error) {
|
||||
team := &kolide.Team{}
|
||||
|
||||
if p.Name == nil {
|
||||
return nil, kolide.NewInvalidArgumentError("name", "missing required argument")
|
||||
}
|
||||
if *p.Name == "" {
|
||||
return nil, kolide.NewInvalidArgumentError("name", "may not be empty")
|
||||
}
|
||||
team.Name = *p.Name
|
||||
|
||||
if p.Description != nil {
|
||||
team.Description = *p.Description
|
||||
}
|
||||
|
||||
if p.Secrets != nil {
|
||||
team.Secrets = p.Secrets
|
||||
} else {
|
||||
// Set up a default enroll secret
|
||||
secret, err := kolide.RandomText(kolide.EnrollSecretDefaultLength)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "generate enroll secret string")
|
||||
}
|
||||
team.Secrets = []*kolide.EnrollSecret{{Secret: secret}}
|
||||
}
|
||||
team, err := svc.ds.NewTeam(team)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return team, nil
|
||||
}
|
||||
|
||||
func (svc *Service) ModifyTeam(ctx context.Context, id uint, payload kolide.TeamPayload) (*kolide.Team, error) {
|
||||
team, err := svc.ds.Team(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if payload.Name != nil {
|
||||
if *payload.Name == "" {
|
||||
return nil, kolide.NewInvalidArgumentError("name", "may not be empty")
|
||||
}
|
||||
team.Name = *payload.Name
|
||||
}
|
||||
if payload.Description != nil {
|
||||
team.Description = *payload.Description
|
||||
}
|
||||
if payload.Secrets != nil {
|
||||
team.Secrets = payload.Secrets
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
}
|
||||
|
||||
func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, id uint, options json.RawMessage) (*kolide.Team, error) {
|
||||
team, err := svc.ds.Team(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
team.AgentOptions = &options
|
||||
} else {
|
||||
team.AgentOptions = nil
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
}
|
||||
|
||||
func (svc *Service) AddTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
idMap := make(map[uint]kolide.TeamUser)
|
||||
for _, user := range users {
|
||||
if !kolide.ValidTeamRole(user.Role) {
|
||||
return nil, kolide.NewInvalidArgumentError("users", fmt.Sprintf("%s is not a valid role for a team user", user.Role))
|
||||
}
|
||||
idMap[user.ID] = user
|
||||
}
|
||||
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Replace existing
|
||||
for i, existingUser := range team.Users {
|
||||
if user, ok := idMap[existingUser.ID]; ok {
|
||||
team.Users[i] = user
|
||||
delete(idMap, user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Add new (that have not already been replaced)
|
||||
for _, user := range idMap {
|
||||
team.Users = append(team.Users, user)
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
}
|
||||
|
||||
func (svc *Service) DeleteTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
idMap := make(map[uint]bool)
|
||||
for _, user := range users {
|
||||
idMap[user.ID] = true
|
||||
}
|
||||
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newUsers := []kolide.TeamUser{}
|
||||
// Delete existing
|
||||
for _, existingUser := range team.Users {
|
||||
if _, ok := idMap[existingUser.ID]; !ok {
|
||||
// Only add non-deleted users
|
||||
newUsers = append(newUsers, existingUser)
|
||||
}
|
||||
}
|
||||
team.Users = newUsers
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
}
|
||||
|
||||
func (svc *Service) ListTeamUsers(ctx context.Context, teamID uint, opt kolide.ListOptions) ([]*kolide.User, error) {
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc.ds.ListUsers(kolide.UserListOptions{ListOptions: opt, TeamID: team.ID})
|
||||
}
|
||||
|
||||
func (svc *Service) ListTeams(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Team, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
filter := kolide.TeamFilter{User: vc.User, IncludeObserver: true}
|
||||
|
||||
return svc.ds.ListTeams(filter, opt)
|
||||
}
|
||||
|
||||
func (svc *Service) DeleteTeam(ctx context.Context, tid uint) error {
|
||||
return svc.ds.DeleteTeam(tid)
|
||||
}
|
||||
|
||||
func (svc *Service) TeamEnrollSecrets(ctx context.Context, teamID uint) ([]*kolide.EnrollSecret, error) {
|
||||
return svc.ds.TeamEnrollSecrets(teamID)
|
||||
}
|
||||
168
server/kolide/errors.go
Normal file
168
server/kolide/errors.go
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
package kolide
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoContext = errors.New("context key not set")
|
||||
ErrMissingLicense = &LicenseError{}
|
||||
)
|
||||
|
||||
// ErrWithInternal is an interface for errors that include extra "internal"
|
||||
// information that should be logged in server logs but not sent to clients.
|
||||
type ErrWithInternal interface {
|
||||
error
|
||||
// Internal returns the error string that must only be logged internally,
|
||||
// not returned to the client.
|
||||
Internal() string
|
||||
}
|
||||
|
||||
// ErrWithStatusCode is an interface for errors that should set a specific HTTP
|
||||
// status when encoding.
|
||||
type ErrWithStatusCode interface {
|
||||
error
|
||||
// StatusCode returns the HTTP status code that should be returned.
|
||||
StatusCode() int
|
||||
}
|
||||
|
||||
// ErrWithRetryAfter is an interface for errors that should set a specific HTTP
|
||||
// Header Retry-After value (see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
|
||||
type ErrWithRetryAfter interface {
|
||||
error
|
||||
// RetryAfter returns the number of seconds to wait before retry.
|
||||
RetryAfter() int
|
||||
}
|
||||
|
||||
// InvalidArgumentError is the error returned when invalid data is presented to
|
||||
// a service method.
|
||||
type InvalidArgumentError []InvalidArgument
|
||||
|
||||
// InvalidArgument is the details about a single invalid argument.
|
||||
type InvalidArgument struct {
|
||||
name string
|
||||
reason string
|
||||
}
|
||||
|
||||
// NewInvalidArgumentError returns a InvalidArgumentError with at least
|
||||
// one error.
|
||||
func NewInvalidArgumentError(name, reason string) *InvalidArgumentError {
|
||||
var invalid InvalidArgumentError
|
||||
invalid = append(invalid, InvalidArgument{
|
||||
name: name,
|
||||
reason: reason,
|
||||
})
|
||||
return &invalid
|
||||
}
|
||||
|
||||
func (e *InvalidArgumentError) Append(name, reason string) {
|
||||
*e = append(*e, InvalidArgument{
|
||||
name: name,
|
||||
reason: reason,
|
||||
})
|
||||
}
|
||||
func (e *InvalidArgumentError) Appendf(name, reasonFmt string, args ...interface{}) {
|
||||
*e = append(*e, InvalidArgument{
|
||||
name: name,
|
||||
reason: fmt.Sprintf(reasonFmt, args...),
|
||||
})
|
||||
}
|
||||
|
||||
func (e *InvalidArgumentError) HasErrors() bool {
|
||||
return len(*e) != 0
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e InvalidArgumentError) Error() string {
|
||||
switch len(e) {
|
||||
case 0:
|
||||
return "validation failed"
|
||||
case 1:
|
||||
return fmt.Sprintf("validation failed: %s %s", e[0].name, e[0].reason)
|
||||
default:
|
||||
return fmt.Sprintf("validation failed: %s %s and %d other errors", e[0].name, e[0].reason,
|
||||
len(e))
|
||||
}
|
||||
}
|
||||
|
||||
func (e InvalidArgumentError) Invalid() []map[string]string {
|
||||
var invalid []map[string]string
|
||||
for _, i := range e {
|
||||
invalid = append(invalid, map[string]string{"name": i.name, "reason": i.reason})
|
||||
}
|
||||
return invalid
|
||||
}
|
||||
|
||||
type AuthFailedError struct {
|
||||
// internal is the reason that should only be logged internally
|
||||
internal string
|
||||
}
|
||||
|
||||
func NewAuthFailedError(internal string) *AuthFailedError {
|
||||
return &AuthFailedError{internal: internal}
|
||||
}
|
||||
|
||||
func (e AuthFailedError) Error() string {
|
||||
return "Authentication failed"
|
||||
}
|
||||
|
||||
func (e AuthFailedError) Internal() string {
|
||||
return e.internal
|
||||
}
|
||||
|
||||
func (e AuthFailedError) StatusCode() int {
|
||||
return http.StatusUnauthorized
|
||||
}
|
||||
|
||||
type AuthRequiredError struct {
|
||||
// internal is the reason that should only be logged internally
|
||||
internal string
|
||||
}
|
||||
|
||||
func NewAuthRequiredError(internal string) *AuthRequiredError {
|
||||
return &AuthRequiredError{internal: internal}
|
||||
}
|
||||
|
||||
func (e AuthRequiredError) Error() string {
|
||||
return "Authentication required"
|
||||
}
|
||||
|
||||
func (e AuthRequiredError) Internal() string {
|
||||
return e.internal
|
||||
}
|
||||
|
||||
func (e AuthRequiredError) StatusCode() int {
|
||||
return http.StatusUnauthorized
|
||||
}
|
||||
|
||||
// PermissionError, set when user is authenticated, but not allowed to perform action
|
||||
type PermissionError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func NewPermissionError(message string) *PermissionError {
|
||||
return &PermissionError{message: message}
|
||||
}
|
||||
|
||||
func (e PermissionError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e PermissionError) PermissionError() []map[string]string {
|
||||
var forbidden []map[string]string
|
||||
return forbidden
|
||||
}
|
||||
|
||||
// LicenseError is returned when the application is not properly licensed.
|
||||
type LicenseError struct{}
|
||||
|
||||
func (e LicenseError) Error() string {
|
||||
return "requires Fleet Basic license"
|
||||
}
|
||||
|
||||
func (e LicenseError) StatusCode() int {
|
||||
return http.StatusPaymentRequired
|
||||
}
|
||||
|
|
@ -13,8 +13,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var errNoContext = errors.New("context key not set")
|
||||
|
||||
// authenticatedHost wraps an endpoint, checks the validity of the node_key
|
||||
// provided in the request, and attaches the corresponding osquery host to the
|
||||
// context for the request
|
||||
|
|
@ -70,7 +68,7 @@ func authenticatedUser(jwtKey string, svc kolide.Service, next endpoint.Endpoint
|
|||
// if not succesful, try again this time with errors
|
||||
bearer, ok := token.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, authRequiredError{internal: "no auth token"}
|
||||
return nil, kolide.NewAuthRequiredError("no auth token")
|
||||
}
|
||||
|
||||
v, err := authViewer(ctx, jwtKey, bearer, svc)
|
||||
|
|
@ -92,30 +90,30 @@ func authViewer(ctx context.Context, jwtKey string, bearerToken token.Token, svc
|
|||
return []byte(jwtKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, authRequiredError{internal: err.Error()}
|
||||
return nil, kolide.NewAuthRequiredError(err.Error())
|
||||
}
|
||||
if !jwtToken.Valid {
|
||||
return nil, authRequiredError{internal: "invalid jwt token"}
|
||||
return nil, kolide.NewAuthRequiredError("invalid jwt token")
|
||||
}
|
||||
claims, ok := jwtToken.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, authRequiredError{internal: "no jwt claims"}
|
||||
return nil, kolide.NewAuthRequiredError("no jwt claims")
|
||||
}
|
||||
sessionKeyClaim, ok := claims["session_key"]
|
||||
if !ok {
|
||||
return nil, authRequiredError{internal: "no session_key in JWT claims"}
|
||||
return nil, kolide.NewAuthRequiredError("no session_key in JWT claims")
|
||||
}
|
||||
sessionKey, ok := sessionKeyClaim.(string)
|
||||
if !ok {
|
||||
return nil, authRequiredError{internal: "non-string key in sessionClaim"}
|
||||
return nil, kolide.NewAuthRequiredError("non-string key in sessionClaim")
|
||||
}
|
||||
session, err := svc.GetSessionByKey(ctx, sessionKey)
|
||||
if err != nil {
|
||||
return nil, authRequiredError{internal: err.Error()}
|
||||
return nil, kolide.NewAuthRequiredError(err.Error())
|
||||
}
|
||||
user, err := svc.User(ctx, session.UserID)
|
||||
if err != nil {
|
||||
return nil, authRequiredError{internal: err.Error()}
|
||||
return nil, kolide.NewAuthRequiredError(err.Error())
|
||||
}
|
||||
return &viewer.Viewer{User: user, Session: session}, nil
|
||||
}
|
||||
|
|
@ -124,10 +122,10 @@ func mustBeAdmin(next endpoint.Endpoint) endpoint.Endpoint {
|
|||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
if !vc.CanPerformAdminActions() {
|
||||
return nil, permissionError{message: "must be an admin"}
|
||||
return nil, kolide.NewPermissionError("must be an admin")
|
||||
}
|
||||
return next(ctx, request)
|
||||
}
|
||||
|
|
@ -137,10 +135,10 @@ func canPerformActions(next endpoint.Endpoint) endpoint.Endpoint {
|
|||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
if !vc.CanPerformActions() {
|
||||
return nil, permissionError{message: "no read permissions"}
|
||||
return nil, kolide.NewPermissionError("no read permissions")
|
||||
}
|
||||
return next(ctx, request)
|
||||
}
|
||||
|
|
@ -150,11 +148,11 @@ func canReadUser(next endpoint.Endpoint) endpoint.Endpoint {
|
|||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
uid := requestUserIDFromContext(ctx)
|
||||
if !vc.CanPerformReadActionOnUser(uid) {
|
||||
return nil, permissionError{message: "no read permissions on user"}
|
||||
return nil, kolide.NewPermissionError("no read permissions on user")
|
||||
}
|
||||
return next(ctx, request)
|
||||
}
|
||||
|
|
@ -164,11 +162,11 @@ func canModifyUser(next endpoint.Endpoint) endpoint.Endpoint {
|
|||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
uid := requestUserIDFromContext(ctx)
|
||||
if !vc.CanPerformWriteActionOnUser(uid) {
|
||||
return nil, permissionError{message: "no write permissions on user"}
|
||||
return nil, kolide.NewPermissionError("no write permissions on user")
|
||||
}
|
||||
return next(ctx, request)
|
||||
}
|
||||
|
|
@ -178,10 +176,10 @@ func canPerformPasswordReset(next endpoint.Endpoint) endpoint.Endpoint {
|
|||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
if !vc.CanPerformPasswordReset() {
|
||||
return nil, permissionError{message: "cannot reset password"}
|
||||
return nil, kolide.NewPermissionError("cannot reset password")
|
||||
}
|
||||
return next(ctx, request)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,15 +64,15 @@ import (
|
|||
// }{
|
||||
// {
|
||||
// endpoint: mustBeAdmin(e),
|
||||
// wantErr: errNoContext,
|
||||
// wantErr: kolide.ErrNoContext,
|
||||
// },
|
||||
// {
|
||||
// endpoint: canReadUser(e),
|
||||
// wantErr: errNoContext,
|
||||
// wantErr: kolide.ErrNoContext,
|
||||
// },
|
||||
// {
|
||||
// endpoint: canModifyUser(e),
|
||||
// wantErr: errNoContext,
|
||||
// wantErr: kolide.ErrNoContext,
|
||||
// },
|
||||
// {
|
||||
// endpoint: mustBeAdmin(e),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func NewLoggingService(svc kolide.Service, logger kitlog.Logger) kolide.Service
|
|||
// loggerDebug returns the the info level if there error is non-nil, otherwise defaulting to the debug level.
|
||||
func (mw loggingMiddleware) loggerDebug(err error) kitlog.Logger {
|
||||
logger := mw.logger
|
||||
if e, ok := err.(ErrWithInternal); ok {
|
||||
if e, ok := err.(kolide.ErrWithInternal); ok {
|
||||
logger = kitlog.With(logger, "err_internal", e.Internal())
|
||||
}
|
||||
if err != nil {
|
||||
|
|
@ -32,7 +32,7 @@ func (mw loggingMiddleware) loggerDebug(err error) kitlog.Logger {
|
|||
// loggerInfo returns the info level
|
||||
func (mw loggingMiddleware) loggerInfo(err error) kitlog.Logger {
|
||||
logger := mw.logger
|
||||
if e, ok := err.(ErrWithInternal); ok {
|
||||
if e, ok := err.(kolide.ErrWithInternal); ok {
|
||||
logger = kitlog.With(logger, "err_internal", e.Internal())
|
||||
}
|
||||
return level.Info(logger)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func (mw loggingMiddleware) InviteNewUser(ctx context.Context, payload kolide.In
|
|||
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
defer func(begin time.Time) {
|
||||
_ = mw.loggerInfo(err).Log(
|
||||
|
|
@ -37,7 +37,7 @@ func (mw loggingMiddleware) DeleteInvite(ctx context.Context, id uint) error {
|
|||
)
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return errNoContext
|
||||
return kolide.ErrNoContext
|
||||
}
|
||||
defer func(begin time.Time) {
|
||||
_ = mw.loggerInfo(err).Log(
|
||||
|
|
@ -58,7 +58,7 @@ func (mw loggingMiddleware) ListInvites(ctx context.Context, opt kolide.ListOpti
|
|||
)
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
defer func(begin time.Time) {
|
||||
_ = mw.loggerInfo(err).Log(
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ func (mw loggingMiddleware) ModifyUser(ctx context.Context, userID uint, p kolid
|
|||
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
|
||||
defer func(begin time.Time) {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ package service
|
|||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/WatchBeam/clock"
|
||||
"github.com/fleetdm/fleet/server/config"
|
||||
|
|
@ -19,6 +17,25 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Service is the struct implementing kolide.Service. Create a new one with NewService.
|
||||
type Service struct {
|
||||
ds kolide.Datastore
|
||||
carveStore kolide.CarveStore
|
||||
resultStore kolide.QueryResultStore
|
||||
liveQueryStore kolide.LiveQueryStore
|
||||
logger kitlog.Logger
|
||||
config config.KolideConfig
|
||||
clock clock.Clock
|
||||
license kolide.LicenseInfo
|
||||
|
||||
osqueryLogWriter *logging.OsqueryLogger
|
||||
|
||||
mailService kolide.MailService
|
||||
ssoSessionStore sso.SessionStore
|
||||
|
||||
seenHostSet *seenHostSet
|
||||
}
|
||||
|
||||
// NewService creates a new service from the config struct
|
||||
func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore,
|
||||
logger kitlog.Logger, config config.KolideConfig, mailService kolide.MailService,
|
||||
|
|
@ -31,7 +48,7 @@ func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore,
|
|||
return nil, errors.Wrap(err, "initializing osquery logging")
|
||||
}
|
||||
|
||||
svc = &service{
|
||||
svc = &Service{
|
||||
ds: ds,
|
||||
carveStore: carveStore,
|
||||
resultStore: resultStore,
|
||||
|
|
@ -43,43 +60,16 @@ func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore,
|
|||
mailService: mailService,
|
||||
ssoSessionStore: sso,
|
||||
seenHostSet: newSeenHostSet(),
|
||||
metaDataClient: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
license: license,
|
||||
license: license,
|
||||
}
|
||||
svc = validationMiddleware{svc, ds, sso}
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
ds kolide.Datastore
|
||||
carveStore kolide.CarveStore
|
||||
resultStore kolide.QueryResultStore
|
||||
liveQueryStore kolide.LiveQueryStore
|
||||
logger kitlog.Logger
|
||||
config config.KolideConfig
|
||||
clock clock.Clock
|
||||
|
||||
osqueryLogWriter *logging.OsqueryLogger
|
||||
|
||||
mailService kolide.MailService
|
||||
ssoSessionStore sso.SessionStore
|
||||
metaDataClient *http.Client
|
||||
|
||||
seenHostSet *seenHostSet
|
||||
|
||||
license kolide.LicenseInfo
|
||||
}
|
||||
|
||||
func (s service) SendEmail(mail kolide.Email) error {
|
||||
func (s Service) SendEmail(mail kolide.Email) error {
|
||||
return s.mailService.SendEmail(mail)
|
||||
}
|
||||
|
||||
func (s service) Clock() clock.Clock {
|
||||
return s.clock
|
||||
}
|
||||
|
||||
type validationMiddleware struct {
|
||||
kolide.Service
|
||||
ds kolide.Datastore
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) AgentOptionsForHost(ctx context.Context, host *kolide.Host) (json.RawMessage, error) {
|
||||
func (svc *Service) AgentOptionsForHost(ctx context.Context, host *kolide.Host) (json.RawMessage, error) {
|
||||
// If host has a team and team has non-empty options, prioritize that.
|
||||
if host.TeamID != nil {
|
||||
team, err := svc.ds.Team(*host.TeamID)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func (e mailError) MailError() []map[string]string {
|
|||
}
|
||||
}
|
||||
|
||||
func (svc service) NewAppConfig(ctx context.Context, p kolide.AppConfigPayload) (*kolide.AppConfig, error) {
|
||||
func (svc Service) NewAppConfig(ctx context.Context, p kolide.AppConfigPayload) (*kolide.AppConfig, error) {
|
||||
config, err := svc.ds.AppConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -61,14 +61,14 @@ func (svc service) NewAppConfig(ctx context.Context, p kolide.AppConfigPayload)
|
|||
return newConfig, nil
|
||||
}
|
||||
|
||||
func (svc service) AppConfig(ctx context.Context) (*kolide.AppConfig, error) {
|
||||
func (svc *Service) AppConfig(ctx context.Context) (*kolide.AppConfig, error) {
|
||||
return svc.ds.AppConfig()
|
||||
}
|
||||
|
||||
func (svc service) SendTestEmail(ctx context.Context, config *kolide.AppConfig) error {
|
||||
func (svc *Service) SendTestEmail(ctx context.Context, config *kolide.AppConfig) error {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return errNoContext
|
||||
return kolide.ErrNoContext
|
||||
}
|
||||
|
||||
testMail := kolide.Email{
|
||||
|
|
@ -88,7 +88,7 @@ func (svc service) SendTestEmail(ctx context.Context, config *kolide.AppConfig)
|
|||
|
||||
}
|
||||
|
||||
func (svc service) ModifyAppConfig(ctx context.Context, p kolide.AppConfigPayload) (*kolide.AppConfig, error) {
|
||||
func (svc *Service) ModifyAppConfig(ctx context.Context, p kolide.AppConfigPayload) (*kolide.AppConfig, error) {
|
||||
oldAppConfig, err := svc.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -240,7 +240,7 @@ func appConfigFromAppConfigPayload(p kolide.AppConfigPayload, config kolide.AppC
|
|||
return &config
|
||||
}
|
||||
|
||||
func (svc service) ApplyEnrollSecretSpec(ctx context.Context, spec *kolide.EnrollSecretSpec) error {
|
||||
func (svc *Service) ApplyEnrollSecretSpec(ctx context.Context, spec *kolide.EnrollSecretSpec) error {
|
||||
for _, s := range spec.Secrets {
|
||||
if s.Secret == "" {
|
||||
return errors.New("enroll secret must not be empty")
|
||||
|
|
@ -250,7 +250,7 @@ func (svc service) ApplyEnrollSecretSpec(ctx context.Context, spec *kolide.Enrol
|
|||
return svc.ds.ApplyEnrollSecrets(nil, spec.Secrets)
|
||||
}
|
||||
|
||||
func (svc service) GetEnrollSecretSpec(ctx context.Context) (*kolide.EnrollSecretSpec, error) {
|
||||
func (svc *Service) GetEnrollSecretSpec(ctx context.Context) (*kolide.EnrollSecretSpec, error) {
|
||||
secrets, err := svc.ds.GetEnrollSecrets(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -258,11 +258,11 @@ func (svc service) GetEnrollSecretSpec(ctx context.Context) (*kolide.EnrollSecre
|
|||
return &kolide.EnrollSecretSpec{Secrets: secrets}, nil
|
||||
}
|
||||
|
||||
func (svc service) Version(ctx context.Context) (*version.Info, error) {
|
||||
func (svc *Service) Version(ctx context.Context) (*version.Info, error) {
|
||||
info := version.Version()
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (svc service) License(ctx context.Context) (*kolide.LicenseInfo, error) {
|
||||
func (svc *Service) License(ctx context.Context) (*kolide.LicenseInfo, error) {
|
||||
return &svc.license, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) NewDistributedQueryCampaignByNames(ctx context.Context, queryString string, queryID *uint, hosts []string, labels []string) (*kolide.DistributedQueryCampaign, error) {
|
||||
func (svc Service) NewDistributedQueryCampaignByNames(ctx context.Context, queryString string, queryID *uint, hosts []string, labels []string) (*kolide.DistributedQueryCampaign, error) {
|
||||
hostIDs, err := svc.ds.HostIDsByName(hosts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "finding host IDs")
|
||||
|
|
@ -31,18 +31,18 @@ func (svc service) NewDistributedQueryCampaignByNames(ctx context.Context, query
|
|||
return svc.NewDistributedQueryCampaign(ctx, queryString, queryID, targets)
|
||||
}
|
||||
|
||||
func (svc service) NewDistributedQueryCampaign(ctx context.Context, queryString string, queryID *uint, targets kolide.HostTargets) (*kolide.DistributedQueryCampaign, error) {
|
||||
func (svc Service) NewDistributedQueryCampaign(ctx context.Context, queryString string, queryID *uint, targets kolide.HostTargets) (*kolide.DistributedQueryCampaign, error) {
|
||||
if err := svc.StatusLiveQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
|
||||
if queryID == nil && queryString == "" {
|
||||
return nil, newInvalidArgumentError("query", "one of query or query_id must be specified")
|
||||
return nil, kolide.NewInvalidArgumentError("query", "one of query or query_id must be specified")
|
||||
}
|
||||
|
||||
var query *kolide.Query
|
||||
|
|
@ -150,7 +150,7 @@ type campaignStatus struct {
|
|||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (svc service) StreamCampaignResults(ctx context.Context, conn *websocket.Conn, campaignID uint) {
|
||||
func (svc Service) StreamCampaignResults(ctx context.Context, conn *websocket.Conn, campaignID uint) {
|
||||
// Find the campaign and ensure it is active
|
||||
campaign, err := svc.ds.DistributedQueryCampaign(campaignID)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const (
|
|||
maxBlockSize = 256 * 1024 * 1024 // 256MB
|
||||
)
|
||||
|
||||
func (svc service) CarveBegin(ctx context.Context, payload kolide.CarveBeginPayload) (*kolide.CarveMetadata, error) {
|
||||
func (svc *Service) CarveBegin(ctx context.Context, payload kolide.CarveBeginPayload) (*kolide.CarveMetadata, error) {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, osqueryError{message: "internal error: missing host from request context"}
|
||||
|
|
@ -66,7 +66,7 @@ func (svc service) CarveBegin(ctx context.Context, payload kolide.CarveBeginPayl
|
|||
return carve, nil
|
||||
}
|
||||
|
||||
func (svc service) CarveBlock(ctx context.Context, payload kolide.CarveBlockPayload) error {
|
||||
func (svc *Service) CarveBlock(ctx context.Context, payload kolide.CarveBlockPayload) error {
|
||||
// Note host did not authenticate via node key. We need to authenticate them
|
||||
// by the session ID and request ID
|
||||
carve, err := svc.carveStore.CarveBySessionId(payload.SessionId)
|
||||
|
|
@ -99,15 +99,15 @@ func (svc service) CarveBlock(ctx context.Context, payload kolide.CarveBlockPayl
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc service) GetCarve(ctx context.Context, id int64) (*kolide.CarveMetadata, error) {
|
||||
func (svc *Service) GetCarve(ctx context.Context, id int64) (*kolide.CarveMetadata, error) {
|
||||
return svc.carveStore.Carve(id)
|
||||
}
|
||||
|
||||
func (svc service) ListCarves(ctx context.Context, opt kolide.CarveListOptions) ([]*kolide.CarveMetadata, error) {
|
||||
func (svc *Service) ListCarves(ctx context.Context, opt kolide.CarveListOptions) ([]*kolide.CarveMetadata, error) {
|
||||
return svc.carveStore.ListCarves(opt)
|
||||
}
|
||||
|
||||
func (svc service) GetBlock(ctx context.Context, carveId, blockId int64) ([]byte, error) {
|
||||
func (svc *Service) GetBlock(ctx context.Context, carveId, blockId int64) ([]byte, error) {
|
||||
metadata, err := svc.carveStore.Carve(carveId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get carve by name")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func TestCarveBegin(t *testing.T) {
|
|||
RequestId: "carve_request",
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
expectedMetadata := kolide.CarveMetadata{
|
||||
ID: 7,
|
||||
HostId: host.ID,
|
||||
|
|
@ -56,7 +56,7 @@ func TestCarveBeginNewCarveError(t *testing.T) {
|
|||
RequestId: "carve_request",
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.NewCarveFunc = func(metadata *kolide.CarveMetadata) (*kolide.CarveMetadata, error) {
|
||||
return nil, fmt.Errorf("ouch!")
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ func TestCarveBeginNewCarveError(t *testing.T) {
|
|||
|
||||
func TestCarveBeginEmptyError(t *testing.T) {
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ctx := hostctx.NewContext(context.Background(), kolide.Host{})
|
||||
|
||||
_, err := svc.CarveBegin(ctx, kolide.CarveBeginPayload{})
|
||||
|
|
@ -80,7 +80,7 @@ func TestCarveBeginEmptyError(t *testing.T) {
|
|||
|
||||
func TestCarveBeginMissingHostError(t *testing.T) {
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
|
||||
_, err := svc.CarveBegin(context.Background(), kolide.CarveBeginPayload{})
|
||||
require.Error(t, err)
|
||||
|
|
@ -96,7 +96,7 @@ func TestCarveBeginBlockSizeMaxError(t *testing.T) {
|
|||
RequestId: "carve_request",
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
|
||||
ctx := hostctx.NewContext(context.Background(), host)
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ func TestCarveBeginCarveSizeMaxError(t *testing.T) {
|
|||
RequestId: "carve_request",
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
|
||||
ctx := hostctx.NewContext(context.Background(), host)
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ func TestCarveBeginCarveSizeError(t *testing.T) {
|
|||
RequestId: "carve_request",
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ctx := hostctx.NewContext(context.Background(), host)
|
||||
|
||||
// Too big
|
||||
|
|
@ -150,7 +150,7 @@ func TestCarveBeginCarveSizeError(t *testing.T) {
|
|||
func TestCarveCarveBlockGetCarveError(t *testing.T) {
|
||||
sessionId := "foobar"
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
return nil, fmt.Errorf("ouch!")
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ func TestCarveCarveBlockRequestIdError(t *testing.T) {
|
|||
SessionId: sessionId,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -206,7 +206,7 @@ func TestCarveCarveBlockBlockCountExceedError(t *testing.T) {
|
|||
SessionId: sessionId,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -237,7 +237,7 @@ func TestCarveCarveBlockBlockCountMatchError(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -268,7 +268,7 @@ func TestCarveCarveBlockBlockSizeError(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -299,7 +299,7 @@ func TestCarveCarveBlockNewBlockError(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -339,7 +339,7 @@ func TestCarveCarveBlock(t *testing.T) {
|
|||
BlockId: 4,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveBySessionIdFunc = func(sessionId string) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.SessionId, sessionId)
|
||||
return metadata, nil
|
||||
|
|
@ -369,7 +369,7 @@ func TestCarveGetBlock(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveFunc = func(carveId int64) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.ID, carveId)
|
||||
return metadata, nil
|
||||
|
|
@ -398,7 +398,7 @@ func TestCarveGetBlockNotAvailableError(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveFunc = func(carveId int64) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.ID, carveId)
|
||||
return metadata, nil
|
||||
|
|
@ -423,7 +423,7 @@ func TestCarveGetBlockGetBlockError(t *testing.T) {
|
|||
MaxBlock: 3,
|
||||
}
|
||||
ms := new(mock.Store)
|
||||
svc := service{carveStore: ms}
|
||||
svc := &Service{carveStore: ms}
|
||||
ms.CarveFunc = func(carveId int64) (*kolide.CarveMetadata, error) {
|
||||
assert.Equal(t, metadata.ID, carveId)
|
||||
return metadata, nil
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
// Certificate returns the PEM encoded certificate chain for osqueryd TLS termination.
|
||||
func (svc service) CertificateChain(ctx context.Context) ([]byte, error) {
|
||||
func (svc *Service) CertificateChain(ctx context.Context) ([]byte, error) {
|
||||
config, err := svc.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -1,173 +1 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrWithInternal is an interface for errors that include extra "internal"
|
||||
// information that should be logged in server logs but not sent to clients.
|
||||
type ErrWithInternal interface {
|
||||
error
|
||||
// Internal returns the error string that must only be logged internally,
|
||||
// not returned to the client.
|
||||
Internal() string
|
||||
}
|
||||
|
||||
// ErrWithStatusCode is an interface for errors that should set a specific HTTP
|
||||
// status when encoding.
|
||||
type ErrWithStatusCode interface {
|
||||
error
|
||||
// StatusCode returns the HTTP status code that should be returned.
|
||||
StatusCode() int
|
||||
}
|
||||
|
||||
// ErrWithRetryAfter is an interface for errors that should set a specific HTTP
|
||||
// Header Retry-After value (see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
|
||||
type ErrWithRetryAfter interface {
|
||||
error
|
||||
// RetryAfter returns the number of seconds to wait before retry.
|
||||
RetryAfter() int
|
||||
}
|
||||
|
||||
type invalidArgumentError []invalidArgument
|
||||
type invalidArgument struct {
|
||||
name string
|
||||
reason string
|
||||
}
|
||||
|
||||
// newInvalidArgumentError returns a invalidArgumentError with at least
|
||||
// one error.
|
||||
func newInvalidArgumentError(name, reason string) *invalidArgumentError {
|
||||
var invalid invalidArgumentError
|
||||
invalid = append(invalid, invalidArgument{
|
||||
name: name,
|
||||
reason: reason,
|
||||
})
|
||||
return &invalid
|
||||
}
|
||||
|
||||
func (e *invalidArgumentError) Append(name, reason string) {
|
||||
*e = append(*e, invalidArgument{
|
||||
name: name,
|
||||
reason: reason,
|
||||
})
|
||||
}
|
||||
func (e *invalidArgumentError) Appendf(name, reasonFmt string, args ...interface{}) {
|
||||
*e = append(*e, invalidArgument{
|
||||
name: name,
|
||||
reason: fmt.Sprintf(reasonFmt, args...),
|
||||
})
|
||||
}
|
||||
|
||||
func (e *invalidArgumentError) HasErrors() bool {
|
||||
return len(*e) != 0
|
||||
}
|
||||
|
||||
// invalidArgumentError is returned when one or more arguments are invalid.
|
||||
func (e invalidArgumentError) Error() string {
|
||||
switch len(e) {
|
||||
case 0:
|
||||
return "validation failed"
|
||||
case 1:
|
||||
return fmt.Sprintf("validation failed: %s %s", e[0].name, e[0].reason)
|
||||
default:
|
||||
return fmt.Sprintf("validation failed: %s %s and %d other errors", e[0].name, e[0].reason,
|
||||
len(e))
|
||||
}
|
||||
}
|
||||
|
||||
func (e invalidArgumentError) Invalid() []map[string]string {
|
||||
var invalid []map[string]string
|
||||
for _, i := range e {
|
||||
invalid = append(invalid, map[string]string{"name": i.name, "reason": i.reason})
|
||||
}
|
||||
return invalid
|
||||
}
|
||||
|
||||
type authFailedError struct {
|
||||
// internal is the reason that should only be logged internally
|
||||
internal string
|
||||
}
|
||||
|
||||
func (e authFailedError) Error() string {
|
||||
return "Authentication failed"
|
||||
}
|
||||
|
||||
func (e authFailedError) Internal() string {
|
||||
return e.internal
|
||||
}
|
||||
|
||||
func (e authFailedError) StatusCode() int {
|
||||
return http.StatusUnauthorized
|
||||
}
|
||||
|
||||
type authRequiredError struct {
|
||||
// internal is the reason that should only be logged internally
|
||||
internal string
|
||||
}
|
||||
|
||||
func (e authRequiredError) Error() string {
|
||||
return "Authentication required"
|
||||
}
|
||||
|
||||
func (e authRequiredError) Internal() string {
|
||||
return e.internal
|
||||
}
|
||||
|
||||
func (e authRequiredError) StatusCode() int {
|
||||
return http.StatusUnauthorized
|
||||
}
|
||||
|
||||
// permissionError, set when user is authenticated, but not allowed to perform action
|
||||
type permissionError struct {
|
||||
message string
|
||||
badArgs []invalidArgument
|
||||
}
|
||||
|
||||
func newPermissionError(name, reason string) permissionError {
|
||||
return permissionError{
|
||||
badArgs: []invalidArgument{
|
||||
invalidArgument{
|
||||
name: name,
|
||||
reason: reason,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e permissionError) Error() string {
|
||||
switch len(e.badArgs) {
|
||||
case 0:
|
||||
case 1:
|
||||
e.message = fmt.Sprintf("unauthorized: %s",
|
||||
e.badArgs[0].reason,
|
||||
)
|
||||
default:
|
||||
e.message = fmt.Sprintf("unauthorized: %s and %d other errors",
|
||||
e.badArgs[0].reason,
|
||||
len(e.badArgs),
|
||||
)
|
||||
}
|
||||
if e.message == "" {
|
||||
return "unauthorized"
|
||||
}
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e permissionError) PermissionError() []map[string]string {
|
||||
var forbidden []map[string]string
|
||||
if len(e.badArgs) == 0 {
|
||||
forbidden = append(forbidden, map[string]string{"reason": e.Error()})
|
||||
return forbidden
|
||||
}
|
||||
for _, arg := range e.badArgs {
|
||||
forbidden = append(forbidden, map[string]string{
|
||||
"name": arg.name,
|
||||
"reason": arg.reason,
|
||||
})
|
||||
}
|
||||
return forbidden
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) ListHosts(ctx context.Context, opt kolide.HostListOptions) ([]*kolide.Host, error) {
|
||||
func (svc Service) ListHosts(ctx context.Context, opt kolide.HostListOptions) ([]*kolide.Host, error) {
|
||||
return svc.ds.ListHosts(opt)
|
||||
}
|
||||
|
||||
func (svc service) GetHost(ctx context.Context, id uint) (*kolide.HostDetail, error) {
|
||||
func (svc Service) GetHost(ctx context.Context, id uint) (*kolide.HostDetail, error) {
|
||||
host, err := svc.ds.Host(id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get host")
|
||||
|
|
@ -20,7 +20,7 @@ func (svc service) GetHost(ctx context.Context, id uint) (*kolide.HostDetail, er
|
|||
return svc.getHostDetails(ctx, host)
|
||||
}
|
||||
|
||||
func (svc service) HostByIdentifier(ctx context.Context, identifier string) (*kolide.HostDetail, error) {
|
||||
func (svc Service) HostByIdentifier(ctx context.Context, identifier string) (*kolide.HostDetail, error) {
|
||||
host, err := svc.ds.HostByIdentifier(identifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get host by identifier")
|
||||
|
|
@ -29,7 +29,7 @@ func (svc service) HostByIdentifier(ctx context.Context, identifier string) (*ko
|
|||
return svc.getHostDetails(ctx, host)
|
||||
}
|
||||
|
||||
func (svc service) getHostDetails(ctx context.Context, host *kolide.Host) (*kolide.HostDetail, error) {
|
||||
func (svc Service) getHostDetails(ctx context.Context, host *kolide.Host) (*kolide.HostDetail, error) {
|
||||
if err := svc.ds.LoadHostSoftware(host); err != nil {
|
||||
return nil, errors.Wrap(err, "load host software")
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ func (svc service) getHostDetails(ctx context.Context, host *kolide.Host) (*koli
|
|||
return &kolide.HostDetail{Host: *host, Labels: labels, Packs: packs}, nil
|
||||
}
|
||||
|
||||
func (svc service) GetHostSummary(ctx context.Context) (*kolide.HostSummary, error) {
|
||||
func (svc Service) GetHostSummary(ctx context.Context) (*kolide.HostSummary, error) {
|
||||
online, offline, mia, new, err := svc.ds.GenerateHostStatusStatistics(svc.clock.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -60,22 +60,22 @@ func (svc service) GetHostSummary(ctx context.Context) (*kolide.HostSummary, err
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteHost(ctx context.Context, id uint) error {
|
||||
func (svc Service) DeleteHost(ctx context.Context, id uint) error {
|
||||
return svc.ds.DeleteHost(id)
|
||||
}
|
||||
|
||||
func (svc *service) FlushSeenHosts(ctx context.Context) error {
|
||||
func (svc *Service) FlushSeenHosts(ctx context.Context) error {
|
||||
hostIDs := svc.seenHostSet.getAndClearHostIDs()
|
||||
return svc.ds.MarkHostsSeen(hostIDs, svc.clock.Now())
|
||||
}
|
||||
|
||||
func (svc service) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs []uint) error {
|
||||
func (svc Service) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs []uint) error {
|
||||
return svc.ds.AddHostsToTeam(teamID, hostIDs)
|
||||
}
|
||||
|
||||
func (svc service) AddHostsToTeamByFilter(ctx context.Context, teamID *uint, opt kolide.HostListOptions, lid *uint) error {
|
||||
func (svc Service) AddHostsToTeamByFilter(ctx context.Context, teamID *uint, opt kolide.HostListOptions, lid *uint) error {
|
||||
if opt.StatusFilter != "" && lid != nil {
|
||||
return newInvalidArgumentError("status", "may not be provided with label_id")
|
||||
return kolide.NewInvalidArgumentError("status", "may not be provided with label_id")
|
||||
}
|
||||
|
||||
opt.PerPage = kolide.PerPageUnlimited
|
||||
|
|
@ -105,7 +105,7 @@ func (svc service) AddHostsToTeamByFilter(ctx context.Context, teamID *uint, opt
|
|||
return svc.ds.AddHostsToTeam(teamID, hostIDs)
|
||||
}
|
||||
|
||||
func (svc *service) RefetchHost(ctx context.Context, id uint) error {
|
||||
func (svc *Service) RefetchHost(ctx context.Context, id uint) error {
|
||||
host, err := svc.ds.Host(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "find host for refetch")
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func TestDeleteHost(t *testing.T) {
|
|||
|
||||
func TestHostDetails(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc := service{ds: ds}
|
||||
svc := &Service{ds: ds}
|
||||
|
||||
host := &kolide.Host{ID: 3}
|
||||
ctx := context.Background()
|
||||
|
|
@ -98,7 +98,7 @@ func TestHostDetails(t *testing.T) {
|
|||
|
||||
func TestRefetchHost(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc := service{ds: ds}
|
||||
svc := &Service{ds: ds}
|
||||
|
||||
host := &kolide.Host{ID: 3}
|
||||
ctx := context.Background()
|
||||
|
|
@ -116,7 +116,7 @@ func TestRefetchHost(t *testing.T) {
|
|||
|
||||
func TestAddHostsToTeamByFilter(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc := service{ds: ds}
|
||||
svc := &Service{ds: ds}
|
||||
ctx := context.Background()
|
||||
|
||||
expectedHostIDs := []uint{1, 2, 4}
|
||||
|
|
@ -140,7 +140,7 @@ func TestAddHostsToTeamByFilter(t *testing.T) {
|
|||
|
||||
func TestAddHostsToTeamByFilterLabel(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc := service{ds: ds}
|
||||
svc := &Service{ds: ds}
|
||||
ctx := context.Background()
|
||||
|
||||
expectedHostIDs := []uint{6}
|
||||
|
|
@ -165,7 +165,7 @@ func TestAddHostsToTeamByFilterLabel(t *testing.T) {
|
|||
|
||||
func TestAddHostsToTeamByFilterEmptyHosts(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc := service{ds: ds}
|
||||
svc := &Service{ds: ds}
|
||||
ctx := context.Background()
|
||||
|
||||
ds.ListHostsFunc = func(opt kolide.HostListOptions) ([]*kolide.Host, error) {
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) InviteNewUser(ctx context.Context, payload kolide.InvitePayload) (*kolide.Invite, error) {
|
||||
func (svc Service) InviteNewUser(ctx context.Context, payload kolide.InvitePayload) (*kolide.Invite, error) {
|
||||
// verify that the user with the given email does not already exist
|
||||
_, err := svc.ds.UserByEmail(*payload.Email)
|
||||
if err == nil {
|
||||
return nil, newInvalidArgumentError("email", "a user with this account already exists")
|
||||
return nil, kolide.NewInvalidArgumentError("email", "a user with this account already exists")
|
||||
}
|
||||
if _, ok := err.(kolide.NotFoundError); !ok {
|
||||
return nil, err
|
||||
|
|
@ -85,29 +85,29 @@ func (svc service) InviteNewUser(ctx context.Context, payload kolide.InvitePaylo
|
|||
return invite, nil
|
||||
}
|
||||
|
||||
func (svc service) ListInvites(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Invite, error) {
|
||||
func (svc *Service) ListInvites(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Invite, error) {
|
||||
return svc.ds.ListInvites(opt)
|
||||
}
|
||||
|
||||
func (svc service) VerifyInvite(ctx context.Context, token string) (*kolide.Invite, error) {
|
||||
func (svc *Service) VerifyInvite(ctx context.Context, token string) (*kolide.Invite, error) {
|
||||
invite, err := svc.ds.InviteByToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if invite.Token != token {
|
||||
return nil, newInvalidArgumentError("invite_token", "Invite Token does not match Email Address.")
|
||||
return nil, kolide.NewInvalidArgumentError("invite_token", "Invite Token does not match Email Address.")
|
||||
}
|
||||
|
||||
expiresAt := invite.CreatedAt.Add(svc.config.App.InviteTokenValidityPeriod)
|
||||
if svc.clock.Now().After(expiresAt) {
|
||||
return nil, newInvalidArgumentError("invite_token", "Invite token has expired.")
|
||||
return nil, kolide.NewInvalidArgumentError("invite_token", "Invite token has expired.")
|
||||
}
|
||||
|
||||
return invite, nil
|
||||
|
||||
}
|
||||
|
||||
func (svc service) DeleteInvite(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteInvite(ctx context.Context, id uint) error {
|
||||
return svc.ds.DeleteInvite(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func TestInviteNewUserMock(t *testing.T) {
|
|||
return i, nil
|
||||
}
|
||||
mailer := &mockMailService{SendEmailFn: func(e kolide.Email) error { return nil }}
|
||||
svc := validationMiddleware{&service{
|
||||
svc := validationMiddleware{&Service{
|
||||
ds: ms,
|
||||
config: config.TestConfig(),
|
||||
mailService: mailer,
|
||||
|
|
@ -53,7 +53,7 @@ func TestInviteNewUserMock(t *testing.T) {
|
|||
|
||||
func TestVerifyInvite(t *testing.T) {
|
||||
ms := new(mock.Store)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ms,
|
||||
config: config.TestConfig(),
|
||||
clock: clock.NewMockClock(),
|
||||
|
|
@ -71,12 +71,11 @@ func TestVerifyInvite(t *testing.T) {
|
|||
},
|
||||
}, nil
|
||||
}
|
||||
wantErr := &invalidArgumentError{{name: "invite_token", reason: "Invite token has expired."}}
|
||||
wantErr := kolide.NewInvalidArgumentError("invite_token", "Invite token has expired.")
|
||||
_, err := svc.VerifyInvite(ctx, "abcd")
|
||||
assert.Equal(t, err, wantErr)
|
||||
|
||||
wantErr = &invalidArgumentError{{name: "invite_token",
|
||||
reason: "Invite Token does not match Email Address."}}
|
||||
wantErr = kolide.NewInvalidArgumentError("invite_token", "Invite Token does not match Email Address.")
|
||||
|
||||
_, err = svc.VerifyInvite(ctx, "bad_token")
|
||||
assert.Equal(t, err, wantErr)
|
||||
|
|
@ -84,7 +83,7 @@ func TestVerifyInvite(t *testing.T) {
|
|||
|
||||
func TestDeleteInvite(t *testing.T) {
|
||||
ms := new(mock.Store)
|
||||
svc := service{ds: ms}
|
||||
svc := &Service{ds: ms}
|
||||
|
||||
ms.DeleteInviteFunc = func(uint) error { return nil }
|
||||
err := svc.DeleteInvite(context.Background(), 1)
|
||||
|
|
@ -94,7 +93,7 @@ func TestDeleteInvite(t *testing.T) {
|
|||
|
||||
func TestListInvites(t *testing.T) {
|
||||
ms := new(mock.Store)
|
||||
svc := service{ds: ms}
|
||||
svc := &Service{ds: ms}
|
||||
|
||||
ms.ListInvitesFunc = func(kolide.ListOptions) ([]*kolide.Invite, error) {
|
||||
return nil, nil
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) ApplyLabelSpecs(ctx context.Context, specs []*kolide.LabelSpec) error {
|
||||
func (svc Service) ApplyLabelSpecs(ctx context.Context, specs []*kolide.LabelSpec) error {
|
||||
for _, spec := range specs {
|
||||
if spec.LabelMembershipType == kolide.LabelMembershipTypeDynamic && len(spec.Hosts) > 0 {
|
||||
return errors.Errorf("label %s is declared as dynamic but contains `hosts` key", spec.Name)
|
||||
|
|
@ -20,24 +20,24 @@ func (svc service) ApplyLabelSpecs(ctx context.Context, specs []*kolide.LabelSpe
|
|||
return svc.ds.ApplyLabelSpecs(specs)
|
||||
}
|
||||
|
||||
func (svc service) GetLabelSpecs(ctx context.Context) ([]*kolide.LabelSpec, error) {
|
||||
func (svc Service) GetLabelSpecs(ctx context.Context) ([]*kolide.LabelSpec, error) {
|
||||
return svc.ds.GetLabelSpecs()
|
||||
}
|
||||
|
||||
func (svc service) GetLabelSpec(ctx context.Context, name string) (*kolide.LabelSpec, error) {
|
||||
func (svc Service) GetLabelSpec(ctx context.Context, name string) (*kolide.LabelSpec, error) {
|
||||
return svc.ds.GetLabelSpec(name)
|
||||
}
|
||||
|
||||
func (svc service) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide.Label, error) {
|
||||
func (svc Service) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide.Label, error) {
|
||||
label := &kolide.Label{}
|
||||
|
||||
if p.Name == nil {
|
||||
return nil, newInvalidArgumentError("name", "missing required argument")
|
||||
return nil, kolide.NewInvalidArgumentError("name", "missing required argument")
|
||||
}
|
||||
label.Name = *p.Name
|
||||
|
||||
if p.Query == nil {
|
||||
return nil, newInvalidArgumentError("query", "missing required argument")
|
||||
return nil, kolide.NewInvalidArgumentError("query", "missing required argument")
|
||||
}
|
||||
label.Query = *p.Query
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ func (svc service) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide
|
|||
return label, nil
|
||||
}
|
||||
|
||||
func (svc service) ModifyLabel(ctx context.Context, id uint, payload kolide.ModifyLabelPayload) (*kolide.Label, error) {
|
||||
func (svc Service) ModifyLabel(ctx context.Context, id uint, payload kolide.ModifyLabelPayload) (*kolide.Label, error) {
|
||||
label, err := svc.ds.Label(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -70,19 +70,19 @@ func (svc service) ModifyLabel(ctx context.Context, id uint, payload kolide.Modi
|
|||
return svc.ds.SaveLabel(label)
|
||||
}
|
||||
|
||||
func (svc service) ListLabels(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Label, error) {
|
||||
func (svc Service) ListLabels(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Label, error) {
|
||||
return svc.ds.ListLabels(opt)
|
||||
}
|
||||
|
||||
func (svc service) GetLabel(ctx context.Context, id uint) (*kolide.Label, error) {
|
||||
func (svc Service) GetLabel(ctx context.Context, id uint) (*kolide.Label, error) {
|
||||
return svc.ds.Label(id)
|
||||
}
|
||||
|
||||
func (svc service) DeleteLabel(ctx context.Context, name string) error {
|
||||
func (svc Service) DeleteLabel(ctx context.Context, name string) error {
|
||||
return svc.ds.DeleteLabel(name)
|
||||
}
|
||||
|
||||
func (svc service) DeleteLabelByID(ctx context.Context, id uint) error {
|
||||
func (svc Service) DeleteLabelByID(ctx context.Context, id uint) error {
|
||||
label, err := svc.ds.Label(id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -90,15 +90,15 @@ func (svc service) DeleteLabelByID(ctx context.Context, id uint) error {
|
|||
return svc.ds.DeleteLabel(label.Name)
|
||||
}
|
||||
|
||||
func (svc service) ListHostsInLabel(ctx context.Context, lid uint, opt kolide.HostListOptions) ([]*kolide.Host, error) {
|
||||
func (svc Service) ListHostsInLabel(ctx context.Context, lid uint, opt kolide.HostListOptions) ([]*kolide.Host, error) {
|
||||
return svc.ds.ListHostsInLabel(lid, opt)
|
||||
}
|
||||
|
||||
func (svc service) ListLabelsForHost(ctx context.Context, hid uint) ([]*kolide.Label, error) {
|
||||
func (svc Service) ListLabelsForHost(ctx context.Context, hid uint) ([]*kolide.Label, error) {
|
||||
return svc.ds.ListLabelsForHost(hid)
|
||||
}
|
||||
|
||||
func (svc service) HostIDsForLabel(lid uint) ([]uint, error) {
|
||||
func (svc *Service) HostIDsForLabel(lid uint) ([]uint, error) {
|
||||
hosts, err := svc.ds.ListHostsInLabel(lid, kolide.HostListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func emptyToZero(val string) string {
|
|||
return val
|
||||
}
|
||||
|
||||
func (svc service) AuthenticateHost(ctx context.Context, nodeKey string) (*kolide.Host, error) {
|
||||
func (svc Service) AuthenticateHost(ctx context.Context, nodeKey string) (*kolide.Host, error) {
|
||||
if nodeKey == "" {
|
||||
return nil, osqueryError{
|
||||
message: "authentication error: missing node key",
|
||||
|
|
@ -77,7 +77,7 @@ func (svc service) AuthenticateHost(ctx context.Context, nodeKey string) (*kolid
|
|||
return host, nil
|
||||
}
|
||||
|
||||
func (svc service) EnrollAgent(ctx context.Context, enrollSecret, hostIdentifier string, hostDetails map[string](map[string]string)) (string, error) {
|
||||
func (svc Service) EnrollAgent(ctx context.Context, enrollSecret, hostIdentifier string, hostDetails map[string](map[string]string)) (string, error) {
|
||||
secret, err := svc.ds.VerifyEnrollSecret(enrollSecret)
|
||||
if err != nil {
|
||||
return "", osqueryError{
|
||||
|
|
@ -191,7 +191,7 @@ func getHostIdentifier(logger log.Logger, identifierOption, providedIdentifier s
|
|||
return providedIdentifier
|
||||
}
|
||||
|
||||
func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{}, error) {
|
||||
func (svc *Service) GetClientConfig(ctx context.Context) (map[string]interface{}, error) {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, osqueryError{message: "internal error: missing host from request context"}
|
||||
|
|
@ -300,14 +300,14 @@ func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{},
|
|||
return config, nil
|
||||
}
|
||||
|
||||
func (svc service) SubmitStatusLogs(ctx context.Context, logs []json.RawMessage) error {
|
||||
func (svc *Service) SubmitStatusLogs(ctx context.Context, logs []json.RawMessage) error {
|
||||
if err := svc.osqueryLogWriter.Status.Write(ctx, logs); err != nil {
|
||||
return osqueryError{message: "error writing status logs: " + err.Error()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) SubmitResultLogs(ctx context.Context, logs []json.RawMessage) error {
|
||||
func (svc *Service) SubmitResultLogs(ctx context.Context, logs []json.RawMessage) error {
|
||||
if err := svc.osqueryLogWriter.Result.Write(ctx, logs); err != nil {
|
||||
return osqueryError{message: "error writing result logs: " + err.Error()}
|
||||
}
|
||||
|
|
@ -859,7 +859,7 @@ func ingestSoftware(logger log.Logger, host *kolide.Host, rows []map[string]stri
|
|||
|
||||
// hostDetailQueries returns the map of queries that should be executed by
|
||||
// osqueryd to fill in the host details
|
||||
func (svc service) hostDetailQueries(host kolide.Host) (map[string]string, error) {
|
||||
func (svc *Service) hostDetailQueries(host kolide.Host) (map[string]string, error) {
|
||||
queries := make(map[string]string)
|
||||
if host.DetailUpdateTime.After(svc.clock.Now().Add(-svc.config.Osquery.DetailUpdateInterval)) && !host.RefetchRequested {
|
||||
// No need to update already fresh details
|
||||
|
|
@ -901,7 +901,7 @@ func (svc service) hostDetailQueries(host kolide.Host) (map[string]string, error
|
|||
return queries, nil
|
||||
}
|
||||
|
||||
func (svc service) GetDistributedQueries(ctx context.Context) (map[string]string, uint, error) {
|
||||
func (svc *Service) GetDistributedQueries(ctx context.Context) (map[string]string, uint, error) {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, 0, osqueryError{message: "internal error: missing host from request context"}
|
||||
|
|
@ -945,7 +945,7 @@ func (svc service) GetDistributedQueries(ctx context.Context) (map[string]string
|
|||
|
||||
// ingestDetailQuery takes the results of a detail query and modifies the
|
||||
// provided kolide.Host appropriately.
|
||||
func (svc service) ingestDetailQuery(host *kolide.Host, name string, rows []map[string]string) error {
|
||||
func (svc *Service) ingestDetailQuery(host *kolide.Host, name string, rows []map[string]string) error {
|
||||
trimmedQuery := strings.TrimPrefix(name, hostDetailQueryPrefix)
|
||||
query, ok := detailQueries[trimmedQuery]
|
||||
if !ok {
|
||||
|
|
@ -966,7 +966,7 @@ func (svc service) ingestDetailQuery(host *kolide.Host, name string, rows []map[
|
|||
}
|
||||
|
||||
// ingestLabelQuery records the results of label queries run by a host
|
||||
func (svc service) ingestLabelQuery(host kolide.Host, query string, rows []map[string]string, results map[uint]bool) error {
|
||||
func (svc *Service) ingestLabelQuery(host kolide.Host, query string, rows []map[string]string, results map[uint]bool) error {
|
||||
trimmedQuery := strings.TrimPrefix(query, hostLabelQueryPrefix)
|
||||
trimmedQueryNum, err := strconv.Atoi(emptyToZero(trimmedQuery))
|
||||
if err != nil {
|
||||
|
|
@ -980,7 +980,7 @@ func (svc service) ingestLabelQuery(host kolide.Host, query string, rows []map[s
|
|||
|
||||
// ingestDistributedQuery takes the results of a distributed query and modifies the
|
||||
// provided kolide.Host appropriately.
|
||||
func (svc service) ingestDistributedQuery(host kolide.Host, name string, rows []map[string]string, failed bool, errMsg string) error {
|
||||
func (svc *Service) ingestDistributedQuery(host kolide.Host, name string, rows []map[string]string, failed bool, errMsg string) error {
|
||||
trimmedQuery := strings.TrimPrefix(name, hostDistributedQueryPrefix)
|
||||
|
||||
campaignID, err := strconv.Atoi(emptyToZero(trimmedQuery))
|
||||
|
|
@ -1045,7 +1045,7 @@ func (svc service) ingestDistributedQuery(host kolide.Host, name string, rows []
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc service) SubmitDistributedQueryResults(ctx context.Context, results kolide.OsqueryDistributedQueryResults, statuses map[string]kolide.OsqueryStatus, messages map[string]string) error {
|
||||
func (svc *Service) SubmitDistributedQueryResults(ctx context.Context, results kolide.OsqueryDistributedQueryResults, statuses map[string]kolide.OsqueryStatus, messages map[string]string) error {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ func TestSubmitStatusLogs(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
|
||||
// Hack to get at the service internals and modify the writer
|
||||
serv := ((svc.(validationMiddleware)).Service).(*service)
|
||||
serv := ((svc.(validationMiddleware)).Service).(*Service)
|
||||
|
||||
testLogger := &testJSONLogger{}
|
||||
serv.osqueryLogWriter = &logging.OsqueryLogger{Status: testLogger}
|
||||
|
|
@ -218,7 +218,7 @@ func TestSubmitResultLogs(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
|
||||
// Hack to get at the service internals and modify the writer
|
||||
serv := ((svc.(validationMiddleware)).Service).(*service)
|
||||
serv := ((svc.(validationMiddleware)).Service).(*Service)
|
||||
|
||||
testLogger := &testJSONLogger{}
|
||||
serv.osqueryLogWriter = &logging.OsqueryLogger{Result: testLogger}
|
||||
|
|
@ -271,7 +271,7 @@ func TestHostDetailQueries(t *testing.T) {
|
|||
UUID: "test_uuid",
|
||||
}
|
||||
|
||||
svc := service{clock: mockClock, config: config.TestConfig(), ds: ds}
|
||||
svc := &Service{clock: mockClock, config: config.TestConfig(), ds: ds}
|
||||
|
||||
queries, err := svc.hostDetailQueries(host)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -1302,7 +1302,7 @@ func TestIngestDistributedQueryParseIdError(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1321,7 +1321,7 @@ func TestIngestDistributedQueryOrphanedCampaignLoadError(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1347,7 +1347,7 @@ func TestIngestDistributedQueryOrphanedCampaignWaitListener(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1380,7 +1380,7 @@ func TestIngestDistributedQueryOrphanedCloseError(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1416,7 +1416,7 @@ func TestIngestDistributedQueryOrphanedStopError(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1453,7 +1453,7 @@ func TestIngestDistributedQueryOrphanedStop(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1490,7 +1490,7 @@ func TestIngestDistributedQueryRecordCompletionError(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
@ -1521,7 +1521,7 @@ func TestIngestDistributedQuery(t *testing.T) {
|
|||
ds := new(mock.Store)
|
||||
rs := pubsub.NewInmemQueryResults()
|
||||
lq := new(live_query.MockLiveQuery)
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
resultStore: rs,
|
||||
liveQueryStore: lq,
|
||||
|
|
|
|||
|
|
@ -6,27 +6,27 @@ import (
|
|||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (svc service) ApplyPackSpecs(ctx context.Context, specs []*kolide.PackSpec) error {
|
||||
func (svc *Service) ApplyPackSpecs(ctx context.Context, specs []*kolide.PackSpec) error {
|
||||
return svc.ds.ApplyPackSpecs(specs)
|
||||
}
|
||||
|
||||
func (svc service) GetPackSpecs(ctx context.Context) ([]*kolide.PackSpec, error) {
|
||||
func (svc *Service) GetPackSpecs(ctx context.Context) ([]*kolide.PackSpec, error) {
|
||||
return svc.ds.GetPackSpecs()
|
||||
}
|
||||
|
||||
func (svc service) GetPackSpec(ctx context.Context, name string) (*kolide.PackSpec, error) {
|
||||
func (svc *Service) GetPackSpec(ctx context.Context, name string) (*kolide.PackSpec, error) {
|
||||
return svc.ds.GetPackSpec(name)
|
||||
}
|
||||
|
||||
func (svc service) ListPacks(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
||||
func (svc *Service) ListPacks(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
||||
return svc.ds.ListPacks(opt)
|
||||
}
|
||||
|
||||
func (svc service) GetPack(ctx context.Context, id uint) (*kolide.Pack, error) {
|
||||
func (svc *Service) GetPack(ctx context.Context, id uint) (*kolide.Pack, error) {
|
||||
return svc.ds.Pack(id)
|
||||
}
|
||||
|
||||
func (svc service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.Pack, error) {
|
||||
func (svc *Service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.Pack, error) {
|
||||
var pack kolide.Pack
|
||||
|
||||
if p.Name != nil {
|
||||
|
|
@ -71,7 +71,7 @@ func (svc service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.P
|
|||
return &pack, nil
|
||||
}
|
||||
|
||||
func (svc service) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload) (*kolide.Pack, error) {
|
||||
func (svc *Service) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload) (*kolide.Pack, error) {
|
||||
pack, err := svc.ds.Pack(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -197,11 +197,11 @@ func (svc service) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload
|
|||
return pack, err
|
||||
}
|
||||
|
||||
func (svc service) DeletePack(ctx context.Context, name string) error {
|
||||
func (svc *Service) DeletePack(ctx context.Context, name string) error {
|
||||
return svc.ds.DeletePack(name)
|
||||
}
|
||||
|
||||
func (svc service) DeletePackByID(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeletePackByID(ctx context.Context, id uint) error {
|
||||
pack, err := svc.ds.Pack(id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -209,34 +209,34 @@ func (svc service) DeletePackByID(ctx context.Context, id uint) error {
|
|||
return svc.ds.DeletePack(pack.Name)
|
||||
}
|
||||
|
||||
func (svc service) AddLabelToPack(ctx context.Context, lid, pid uint) error {
|
||||
func (svc *Service) AddLabelToPack(ctx context.Context, lid, pid uint) error {
|
||||
return svc.ds.AddLabelToPack(lid, pid)
|
||||
}
|
||||
|
||||
func (svc service) RemoveLabelFromPack(ctx context.Context, lid, pid uint) error {
|
||||
func (svc *Service) RemoveLabelFromPack(ctx context.Context, lid, pid uint) error {
|
||||
return svc.ds.RemoveLabelFromPack(lid, pid)
|
||||
}
|
||||
|
||||
func (svc service) AddHostToPack(ctx context.Context, hid, pid uint) error {
|
||||
func (svc *Service) AddHostToPack(ctx context.Context, hid, pid uint) error {
|
||||
return svc.ds.AddHostToPack(hid, pid)
|
||||
}
|
||||
|
||||
func (svc service) RemoveHostFromPack(ctx context.Context, hid, pid uint) error {
|
||||
func (svc *Service) RemoveHostFromPack(ctx context.Context, hid, pid uint) error {
|
||||
return svc.ds.RemoveHostFromPack(hid, pid)
|
||||
}
|
||||
|
||||
func (svc service) ListLabelsForPack(ctx context.Context, pid uint) ([]*kolide.Label, error) {
|
||||
func (svc *Service) ListLabelsForPack(ctx context.Context, pid uint) ([]*kolide.Label, error) {
|
||||
return svc.ds.ListLabelsForPack(pid)
|
||||
}
|
||||
|
||||
func (svc service) ListHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]uint, error) {
|
||||
func (svc *Service) ListHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]uint, error) {
|
||||
return svc.ds.ListHostsInPack(pid, opt)
|
||||
}
|
||||
|
||||
func (svc service) ListExplicitHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]uint, error) {
|
||||
func (svc *Service) ListExplicitHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]uint, error) {
|
||||
return svc.ds.ListExplicitHostsInPack(pid, opt)
|
||||
}
|
||||
|
||||
func (svc service) ListPacksForHost(ctx context.Context, hid uint) ([]*kolide.Pack, error) {
|
||||
func (svc *Service) ListPacksForHost(ctx context.Context, hid uint) ([]*kolide.Pack, error) {
|
||||
return svc.ds.ListPacksForHost(hid)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func specFromQuery(query *kolide.Query) *kolide.QuerySpec {
|
|||
}
|
||||
}
|
||||
|
||||
func (svc service) ApplyQuerySpecs(ctx context.Context, specs []*kolide.QuerySpec) error {
|
||||
func (svc Service) ApplyQuerySpecs(ctx context.Context, specs []*kolide.QuerySpec) error {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return errors.New("user must be authenticated to apply queries")
|
||||
|
|
@ -46,7 +46,7 @@ func (svc service) ApplyQuerySpecs(ctx context.Context, specs []*kolide.QuerySpe
|
|||
return errors.Wrap(err, "applying queries")
|
||||
}
|
||||
|
||||
func (svc service) GetQuerySpecs(ctx context.Context) ([]*kolide.QuerySpec, error) {
|
||||
func (svc Service) GetQuerySpecs(ctx context.Context) ([]*kolide.QuerySpec, error) {
|
||||
queries, err := svc.ds.ListQueries(kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting queries")
|
||||
|
|
@ -59,7 +59,7 @@ func (svc service) GetQuerySpecs(ctx context.Context) ([]*kolide.QuerySpec, erro
|
|||
return specs, nil
|
||||
}
|
||||
|
||||
func (svc service) GetQuerySpec(ctx context.Context, name string) (*kolide.QuerySpec, error) {
|
||||
func (svc Service) GetQuerySpec(ctx context.Context, name string) (*kolide.QuerySpec, error) {
|
||||
query, err := svc.ds.QueryByName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -67,15 +67,15 @@ func (svc service) GetQuerySpec(ctx context.Context, name string) (*kolide.Query
|
|||
return specFromQuery(query), nil
|
||||
}
|
||||
|
||||
func (svc service) ListQueries(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Query, error) {
|
||||
func (svc Service) ListQueries(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Query, error) {
|
||||
return svc.ds.ListQueries(opt)
|
||||
}
|
||||
|
||||
func (svc service) GetQuery(ctx context.Context, id uint) (*kolide.Query, error) {
|
||||
func (svc *Service) GetQuery(ctx context.Context, id uint) (*kolide.Query, error) {
|
||||
return svc.ds.Query(id)
|
||||
}
|
||||
|
||||
func (svc service) NewQuery(ctx context.Context, p kolide.QueryPayload) (*kolide.Query, error) {
|
||||
func (svc *Service) NewQuery(ctx context.Context, p kolide.QueryPayload) (*kolide.Query, error) {
|
||||
query := &kolide.Query{Saved: true}
|
||||
|
||||
if p.Name != nil {
|
||||
|
|
@ -112,7 +112,7 @@ func (svc service) NewQuery(ctx context.Context, p kolide.QueryPayload) (*kolide
|
|||
return query, nil
|
||||
}
|
||||
|
||||
func (svc service) ModifyQuery(ctx context.Context, id uint, p kolide.QueryPayload) (*kolide.Query, error) {
|
||||
func (svc *Service) ModifyQuery(ctx context.Context, id uint, p kolide.QueryPayload) (*kolide.Query, error) {
|
||||
query, err := svc.ds.Query(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -145,11 +145,11 @@ func (svc service) ModifyQuery(ctx context.Context, id uint, p kolide.QueryPaylo
|
|||
return query, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteQuery(ctx context.Context, name string) error {
|
||||
func (svc *Service) DeleteQuery(ctx context.Context, name string) error {
|
||||
return svc.ds.DeleteQuery(name)
|
||||
}
|
||||
|
||||
func (svc service) DeleteQueryByID(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteQueryByID(ctx context.Context, id uint) error {
|
||||
query, err := svc.ds.Query(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "lookup query by ID")
|
||||
|
|
@ -158,6 +158,6 @@ func (svc service) DeleteQueryByID(ctx context.Context, id uint) error {
|
|||
return errors.Wrap(svc.ds.DeleteQuery(query.Name), "delete query")
|
||||
}
|
||||
|
||||
func (svc service) DeleteQueries(ctx context.Context, ids []uint) (uint, error) {
|
||||
func (svc *Service) DeleteQueries(ctx context.Context, ids []uint) (uint, error) {
|
||||
return svc.ds.DeleteQueries(ids)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) GetScheduledQueriesInPack(ctx context.Context, id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) {
|
||||
func (svc *Service) GetScheduledQueriesInPack(ctx context.Context, id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) {
|
||||
return svc.ds.ListScheduledQueriesInPack(id, opts)
|
||||
}
|
||||
|
||||
func (svc service) GetScheduledQuery(ctx context.Context, id uint) (*kolide.ScheduledQuery, error) {
|
||||
func (svc *Service) GetScheduledQuery(ctx context.Context, id uint) (*kolide.ScheduledQuery, error) {
|
||||
return svc.ds.ScheduledQuery(id)
|
||||
}
|
||||
|
||||
func (svc service) ScheduleQuery(ctx context.Context, sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) {
|
||||
func (svc *Service) ScheduleQuery(ctx context.Context, sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) {
|
||||
// Fill in the name with query name if it is unset (because the UI
|
||||
// doesn't provide a way to set it)
|
||||
if sq.Name == "" {
|
||||
|
|
@ -52,7 +52,7 @@ func findNextNameForQuery(name string, scheduled []*kolide.ScheduledQuery) strin
|
|||
return name
|
||||
}
|
||||
|
||||
func (svc service) ModifyScheduledQuery(ctx context.Context, id uint, p kolide.ScheduledQueryPayload) (*kolide.ScheduledQuery, error) {
|
||||
func (svc *Service) ModifyScheduledQuery(ctx context.Context, id uint, p kolide.ScheduledQueryPayload) (*kolide.ScheduledQuery, error) {
|
||||
sq, err := svc.GetScheduledQuery(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting scheduled query to modify")
|
||||
|
|
@ -98,6 +98,6 @@ func (svc service) ModifyScheduledQuery(ctx context.Context, id uint, p kolide.S
|
|||
return svc.ds.SaveScheduledQuery(sq)
|
||||
}
|
||||
|
||||
func (svc service) DeleteScheduledQuery(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteScheduledQuery(ctx context.Context, id uint) error {
|
||||
return svc.ds.DeleteScheduledQuery(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) SSOSettings(ctx context.Context) (*kolide.SSOSettings, error) {
|
||||
func (svc Service) SSOSettings(ctx context.Context) (*kolide.SSOSettings, error) {
|
||||
appConfig, err := svc.ds.AppConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SSOSettings getting app config")
|
||||
|
|
@ -29,7 +29,7 @@ func (svc service) SSOSettings(ctx context.Context) (*kolide.SSOSettings, error)
|
|||
return settings, nil
|
||||
}
|
||||
|
||||
func (svc service) InitiateSSO(ctx context.Context, redirectURL string) (string, error) {
|
||||
func (svc *Service) InitiateSSO(ctx context.Context, redirectURL string) (string, error) {
|
||||
appConfig, err := svc.ds.AppConfig()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "InitiateSSO getting app config")
|
||||
|
|
@ -67,9 +67,9 @@ func (svc service) InitiateSSO(ctx context.Context, redirectURL string) (string,
|
|||
return idpURL, nil
|
||||
}
|
||||
|
||||
func (svc service) getMetadata(config *kolide.AppConfig) (*sso.Metadata, error) {
|
||||
func (svc *Service) getMetadata(config *kolide.AppConfig) (*sso.Metadata, error) {
|
||||
if config.MetadataURL != "" {
|
||||
metadata, err := sso.GetMetadata(config.MetadataURL, svc.metaDataClient)
|
||||
metadata, err := sso.GetMetadata(config.MetadataURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ func (svc service) getMetadata(config *kolide.AppConfig) (*sso.Metadata, error)
|
|||
return nil, errors.Errorf("missing metadata for idp %s", config.IDPName)
|
||||
}
|
||||
|
||||
func (svc service) CallbackSSO(ctx context.Context, auth kolide.Auth) (*kolide.SSOSession, error) {
|
||||
func (svc *Service) CallbackSSO(ctx context.Context, auth kolide.Auth) (*kolide.SSOSession, error) {
|
||||
appConfig, err := svc.ds.AppConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get config for sso")
|
||||
|
|
@ -156,7 +156,7 @@ func (svc service) CallbackSSO(ctx context.Context, auth kolide.Auth) (*kolide.S
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (svc service) Login(ctx context.Context, username, password string) (*kolide.User, string, error) {
|
||||
func (svc *Service) Login(ctx context.Context, username, password string) (*kolide.User, string, error) {
|
||||
// If there is an error, sleep until the request has taken at least 1
|
||||
// second. This means that generally a login failure for any reason will
|
||||
// take ~1s and frustrate a timing attack.
|
||||
|
|
@ -169,29 +169,29 @@ func (svc service) Login(ctx context.Context, username, password string) (*kolid
|
|||
|
||||
user, err := svc.userByEmailOrUsername(username)
|
||||
if _, ok := err.(kolide.NotFoundError); ok {
|
||||
return nil, "", authFailedError{internal: "user not found"}
|
||||
return nil, "", kolide.NewAuthFailedError("user not found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", authFailedError{internal: err.Error()}
|
||||
return nil, "", kolide.NewAuthFailedError(err.Error())
|
||||
}
|
||||
|
||||
if err = user.ValidatePassword(password); err != nil {
|
||||
return nil, "", authFailedError{internal: "invalid password"}
|
||||
return nil, "", kolide.NewAuthFailedError("invalid password")
|
||||
}
|
||||
|
||||
if user.SSOEnabled {
|
||||
return nil, "", authFailedError{internal: "password login disabled for sso users"}
|
||||
return nil, "", kolide.NewAuthFailedError("password login disabled for sso users")
|
||||
}
|
||||
|
||||
token, err := svc.makeSession(user.ID)
|
||||
if err != nil {
|
||||
return nil, "", authFailedError{internal: err.Error()}
|
||||
return nil, "", kolide.NewAuthFailedError(err.Error())
|
||||
}
|
||||
|
||||
return user, token, nil
|
||||
}
|
||||
|
||||
func (svc service) userByEmailOrUsername(username string) (*kolide.User, error) {
|
||||
func (svc *Service) userByEmailOrUsername(username string) (*kolide.User, error) {
|
||||
if strings.Contains(username, "@") {
|
||||
return svc.ds.UserByEmail(username)
|
||||
}
|
||||
|
|
@ -199,7 +199,7 @@ func (svc service) userByEmailOrUsername(username string) (*kolide.User, error)
|
|||
}
|
||||
|
||||
// makeSession is a helper that creates a new session after authentication
|
||||
func (svc service) makeSession(id uint) (string, error) {
|
||||
func (svc *Service) makeSession(id uint) (string, error) {
|
||||
sessionKeySize := svc.config.Session.KeySize
|
||||
key := make([]byte, sessionKeySize)
|
||||
_, err := rand.Read(key)
|
||||
|
|
@ -226,15 +226,15 @@ func (svc service) makeSession(id uint) (string, error) {
|
|||
return tokenString, nil
|
||||
}
|
||||
|
||||
func (svc service) Logout(ctx context.Context) error {
|
||||
func (svc *Service) Logout(ctx context.Context) error {
|
||||
// this should not return an error if the user wasn't logged in
|
||||
return svc.DestroySession(ctx)
|
||||
}
|
||||
|
||||
func (svc service) DestroySession(ctx context.Context) error {
|
||||
func (svc *Service) DestroySession(ctx context.Context) error {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return errNoContext
|
||||
return kolide.ErrNoContext
|
||||
}
|
||||
|
||||
session, err := svc.ds.SessionByID(vc.SessionID())
|
||||
|
|
@ -245,7 +245,7 @@ func (svc service) DestroySession(ctx context.Context) error {
|
|||
return svc.ds.DestroySession(session)
|
||||
}
|
||||
|
||||
func (svc service) GetInfoAboutSessionsForUser(ctx context.Context, id uint) ([]*kolide.Session, error) {
|
||||
func (svc *Service) GetInfoAboutSessionsForUser(ctx context.Context, id uint) ([]*kolide.Session, error) {
|
||||
var validatedSessions []*kolide.Session
|
||||
|
||||
sessions, err := svc.ds.ListSessionsForUser(id)
|
||||
|
|
@ -262,11 +262,11 @@ func (svc service) GetInfoAboutSessionsForUser(ctx context.Context, id uint) ([]
|
|||
return validatedSessions, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteSessionsForUser(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteSessionsForUser(ctx context.Context, id uint) error {
|
||||
return svc.ds.DestroyAllSessionsForUser(id)
|
||||
}
|
||||
|
||||
func (svc service) GetInfoAboutSession(ctx context.Context, id uint) (*kolide.Session, error) {
|
||||
func (svc *Service) GetInfoAboutSession(ctx context.Context, id uint) (*kolide.Session, error) {
|
||||
session, err := svc.ds.SessionByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -280,7 +280,7 @@ func (svc service) GetInfoAboutSession(ctx context.Context, id uint) (*kolide.Se
|
|||
return session, nil
|
||||
}
|
||||
|
||||
func (svc service) GetSessionByKey(ctx context.Context, key string) (*kolide.Session, error) {
|
||||
func (svc *Service) GetSessionByKey(ctx context.Context, key string) (*kolide.Session, error) {
|
||||
session, err := svc.ds.SessionByKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -294,7 +294,7 @@ func (svc service) GetSessionByKey(ctx context.Context, key string) (*kolide.Ses
|
|||
return session, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteSession(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteSession(ctx context.Context, id uint) error {
|
||||
session, err := svc.ds.SessionByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -302,9 +302,9 @@ func (svc service) DeleteSession(ctx context.Context, id uint) error {
|
|||
return svc.ds.DestroySession(session)
|
||||
}
|
||||
|
||||
func (svc service) validateSession(session *kolide.Session) error {
|
||||
func (svc *Service) validateSession(session *kolide.Session) error {
|
||||
if session == nil {
|
||||
return authRequiredError{internal: "active session not present"}
|
||||
return kolide.NewAuthRequiredError("active session not present")
|
||||
}
|
||||
|
||||
sessionDuration := svc.config.Session.Duration
|
||||
|
|
@ -314,7 +314,7 @@ func (svc service) validateSession(session *kolide.Session) error {
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "destroying session")
|
||||
}
|
||||
return authRequiredError{internal: "expired session"}
|
||||
return kolide.NewAuthRequiredError("expired session")
|
||||
}
|
||||
|
||||
return svc.ds.MarkSessionAccessed(session)
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) StatusResultStore(ctx context.Context) error {
|
||||
func (svc *Service) StatusResultStore(ctx context.Context) error {
|
||||
return svc.resultStore.HealthCheck()
|
||||
}
|
||||
|
||||
func (svc service) StatusLiveQuery(ctx context.Context) error {
|
||||
func (svc *Service) StatusLiveQuery(ctx context.Context) error {
|
||||
cfg, err := svc.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "retreiving app config")
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import (
|
|||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (svc service) SearchTargets(ctx context.Context, matchQuery string, queryID *uint, targets kolide.HostTargets) (*kolide.TargetSearchResults, error) {
|
||||
func (svc Service) SearchTargets(ctx context.Context, matchQuery string, queryID *uint, targets kolide.HostTargets) (*kolide.TargetSearchResults, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
|
||||
includeObserver := false
|
||||
|
|
@ -50,10 +50,10 @@ func (svc service) SearchTargets(ctx context.Context, matchQuery string, queryID
|
|||
return results, nil
|
||||
}
|
||||
|
||||
func (svc service) CountHostsInTargets(ctx context.Context, queryID *uint, targets kolide.HostTargets) (*kolide.TargetMetrics, error) {
|
||||
func (svc Service) CountHostsInTargets(ctx context.Context, queryID *uint, targets kolide.HostTargets) (*kolide.TargetMetrics, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
|
||||
includeObserver := false
|
||||
|
|
|
|||
|
|
@ -3,158 +3,42 @@ package service
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/fleetdm/fleet/server/contexts/viewer"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) NewTeam(ctx context.Context, p kolide.TeamPayload) (*kolide.Team, error) {
|
||||
team := &kolide.Team{}
|
||||
|
||||
if p.Name == nil {
|
||||
return nil, newInvalidArgumentError("name", "missing required argument")
|
||||
}
|
||||
if *p.Name == "" {
|
||||
return nil, newInvalidArgumentError("name", "may not be empty")
|
||||
}
|
||||
team.Name = *p.Name
|
||||
|
||||
if p.Description != nil {
|
||||
team.Description = *p.Description
|
||||
}
|
||||
|
||||
if p.Secrets != nil {
|
||||
team.Secrets = p.Secrets
|
||||
} else {
|
||||
// Set up a default enroll secret
|
||||
secret, err := kolide.RandomText(kolide.EnrollSecretDefaultLength)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "generate enroll secret string")
|
||||
}
|
||||
team.Secrets = []*kolide.EnrollSecret{{Secret: secret}}
|
||||
}
|
||||
team, err := svc.ds.NewTeam(team)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return team, nil
|
||||
func (svc *Service) NewTeam(ctx context.Context, p kolide.TeamPayload) (*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) ModifyTeam(ctx context.Context, id uint, payload kolide.TeamPayload) (*kolide.Team, error) {
|
||||
team, err := svc.ds.Team(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if payload.Name != nil {
|
||||
if *payload.Name == "" {
|
||||
return nil, newInvalidArgumentError("name", "may not be empty")
|
||||
}
|
||||
team.Name = *payload.Name
|
||||
}
|
||||
if payload.Description != nil {
|
||||
team.Description = *payload.Description
|
||||
}
|
||||
if payload.Secrets != nil {
|
||||
team.Secrets = payload.Secrets
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
func (svc *Service) ModifyTeam(ctx context.Context, id uint, payload kolide.TeamPayload) (*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) ModifyTeamAgentOptions(ctx context.Context, id uint, options json.RawMessage) (*kolide.Team, error) {
|
||||
team, err := svc.ds.Team(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
team.AgentOptions = &options
|
||||
} else {
|
||||
team.AgentOptions = nil
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, id uint, options json.RawMessage) (*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) AddTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
idMap := make(map[uint]kolide.TeamUser)
|
||||
for _, user := range users {
|
||||
if !kolide.ValidTeamRole(user.Role) {
|
||||
return nil, newInvalidArgumentError("users", fmt.Sprintf("%s is not a valid role for a team user", user.Role))
|
||||
}
|
||||
idMap[user.ID] = user
|
||||
}
|
||||
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Replace existing
|
||||
for i, existingUser := range team.Users {
|
||||
if user, ok := idMap[existingUser.ID]; ok {
|
||||
team.Users[i] = user
|
||||
delete(idMap, user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Add new (that have not already been replaced)
|
||||
for _, user := range idMap {
|
||||
team.Users = append(team.Users, user)
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
func (svc *Service) AddTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) DeleteTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
idMap := make(map[uint]bool)
|
||||
for _, user := range users {
|
||||
idMap[user.ID] = true
|
||||
}
|
||||
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newUsers := []kolide.TeamUser{}
|
||||
// Delete existing
|
||||
for _, existingUser := range team.Users {
|
||||
if _, ok := idMap[existingUser.ID]; !ok {
|
||||
// Only add non-deleted users
|
||||
newUsers = append(newUsers, existingUser)
|
||||
}
|
||||
}
|
||||
team.Users = newUsers
|
||||
|
||||
return svc.ds.SaveTeam(team)
|
||||
func (svc *Service) DeleteTeamUsers(ctx context.Context, teamID uint, users []kolide.TeamUser) (*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) ListTeamUsers(ctx context.Context, teamID uint, opt kolide.ListOptions) ([]*kolide.User, error) {
|
||||
team, err := svc.ds.Team(teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc.ds.ListUsers(kolide.UserListOptions{ListOptions: opt, TeamID: team.ID})
|
||||
func (svc *Service) ListTeamUsers(ctx context.Context, teamID uint, opt kolide.ListOptions) ([]*kolide.User, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) ListTeams(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Team, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
}
|
||||
filter := kolide.TeamFilter{User: vc.User, IncludeObserver: true}
|
||||
|
||||
return svc.ds.ListTeams(filter, opt)
|
||||
func (svc *Service) ListTeams(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Team, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) DeleteTeam(ctx context.Context, tid uint) error {
|
||||
return svc.ds.DeleteTeam(tid)
|
||||
func (svc *Service) DeleteTeam(ctx context.Context, tid uint) error {
|
||||
return kolide.ErrMissingLicense
|
||||
}
|
||||
|
||||
func (svc service) TeamEnrollSecrets(ctx context.Context, teamID uint) ([]*kolide.EnrollSecret, error) {
|
||||
return svc.ds.TeamEnrollSecrets(teamID)
|
||||
func (svc *Service) TeamEnrollSecrets(ctx context.Context, teamID uint) ([]*kolide.EnrollSecret, error) {
|
||||
return nil, kolide.ErrMissingLicense
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) CreateUserWithInvite(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
func (svc Service) CreateUserWithInvite(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
invite, err := svc.VerifyInvite(ctx, *p.InviteToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -35,11 +35,11 @@ func (svc service) CreateUserWithInvite(ctx context.Context, p kolide.UserPayloa
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) CreateUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
func (svc Service) CreateUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
return svc.newUser(p)
|
||||
}
|
||||
|
||||
func (svc service) newUser(p kolide.UserPayload) (*kolide.User, error) {
|
||||
func (svc Service) newUser(p kolide.UserPayload) (*kolide.User, error) {
|
||||
var ssoEnabled bool
|
||||
// if user is SSO generate a fake password
|
||||
if (p.SSOInvite != nil && *p.SSOInvite) || (p.SSOEnabled != nil && *p.SSOEnabled) {
|
||||
|
|
@ -62,12 +62,12 @@ func (svc service) newUser(p kolide.UserPayload) (*kolide.User, error) {
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) ChangeUserAdmin(ctx context.Context, id uint, isAdmin bool) (*kolide.User, error) {
|
||||
func (svc *Service) ChangeUserAdmin(ctx context.Context, id uint, isAdmin bool) (*kolide.User, error) {
|
||||
// TODO remove this function
|
||||
return nil, errors.New("This function is being eliminated")
|
||||
}
|
||||
|
||||
func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPayload) (*kolide.User, error) {
|
||||
func (svc *Service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPayload) (*kolide.User, error) {
|
||||
user, err := svc.User(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -104,7 +104,7 @@ func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPay
|
|||
|
||||
if p.GlobalRole != nil && *p.GlobalRole != "" {
|
||||
if p.Teams != nil && len(*p.Teams) > 0 {
|
||||
return nil, newInvalidArgumentError("teams", "may not be specified with global_role")
|
||||
return nil, kolide.NewInvalidArgumentError("teams", "may not be specified with global_role")
|
||||
}
|
||||
user.GlobalRole = p.GlobalRole
|
||||
user.Teams = []kolide.UserTeam{}
|
||||
|
|
@ -121,12 +121,12 @@ func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPay
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) modifyEmailAddress(ctx context.Context, user *kolide.User, email string, password *string) error {
|
||||
func (svc *Service) modifyEmailAddress(ctx context.Context, user *kolide.User, email string, password *string) error {
|
||||
// password requirement handled in validation middleware
|
||||
if password != nil {
|
||||
err := user.ValidatePassword(*password)
|
||||
if err != nil {
|
||||
return newPermissionError("password", "incorrect password")
|
||||
return kolide.NewPermissionError("incorrect password")
|
||||
}
|
||||
}
|
||||
random, err := kolide.RandomText(svc.config.App.TokenKeySize)
|
||||
|
|
@ -155,41 +155,41 @@ func (svc service) modifyEmailAddress(ctx context.Context, user *kolide.User, em
|
|||
return svc.mailService.SendEmail(changeEmail)
|
||||
}
|
||||
|
||||
func (svc service) DeleteUser(ctx context.Context, id uint) error {
|
||||
func (svc *Service) DeleteUser(ctx context.Context, id uint) error {
|
||||
return svc.ds.DeleteUser(id)
|
||||
}
|
||||
|
||||
func (svc service) ChangeUserEmail(ctx context.Context, token string) (string, error) {
|
||||
func (svc *Service) ChangeUserEmail(ctx context.Context, token string) (string, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return "", errNoContext
|
||||
return "", kolide.ErrNoContext
|
||||
}
|
||||
return svc.ds.ConfirmPendingEmailChange(vc.UserID(), token)
|
||||
}
|
||||
|
||||
func (svc service) User(ctx context.Context, id uint) (*kolide.User, error) {
|
||||
func (svc *Service) User(ctx context.Context, id uint) (*kolide.User, error) {
|
||||
return svc.ds.UserByID(id)
|
||||
}
|
||||
|
||||
func (svc service) AuthenticatedUser(ctx context.Context) (*kolide.User, error) {
|
||||
func (svc *Service) AuthenticatedUser(ctx context.Context) (*kolide.User, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
if !vc.IsLoggedIn() {
|
||||
return nil, permissionError{}
|
||||
return nil, kolide.NewPermissionError("not logged in")
|
||||
}
|
||||
return vc.User, nil
|
||||
}
|
||||
|
||||
func (svc service) ListUsers(ctx context.Context, opt kolide.UserListOptions) ([]*kolide.User, error) {
|
||||
func (svc *Service) ListUsers(ctx context.Context, opt kolide.UserListOptions) ([]*kolide.User, error) {
|
||||
return svc.ds.ListUsers(opt)
|
||||
}
|
||||
|
||||
// setNewPassword is a helper for changing a user's password. It should be
|
||||
// called to set the new password after proper authorization has been
|
||||
// performed.
|
||||
func (svc service) setNewPassword(ctx context.Context, user *kolide.User, password string) error {
|
||||
func (svc *Service) setNewPassword(ctx context.Context, user *kolide.User, password string) error {
|
||||
err := user.SetPassword(password, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "setting new password")
|
||||
|
|
@ -205,20 +205,20 @@ func (svc service) setNewPassword(ctx context.Context, user *kolide.User, passwo
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc service) ChangePassword(ctx context.Context, oldPass, newPass string) error {
|
||||
func (svc *Service) ChangePassword(ctx context.Context, oldPass, newPass string) error {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return errNoContext
|
||||
return kolide.ErrNoContext
|
||||
}
|
||||
if vc.User.SSOEnabled {
|
||||
return errors.New("change password for single sign on user not allowed")
|
||||
}
|
||||
if err := vc.User.ValidatePassword(newPass); err == nil {
|
||||
return newInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
return kolide.NewInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
}
|
||||
|
||||
if err := vc.User.ValidatePassword(oldPass); err != nil {
|
||||
return newInvalidArgumentError("old_password", "old password does not match")
|
||||
return kolide.NewInvalidArgumentError("old_password", "old password does not match")
|
||||
}
|
||||
|
||||
if err := svc.setNewPassword(ctx, vc.User, newPass); err != nil {
|
||||
|
|
@ -227,7 +227,7 @@ func (svc service) ChangePassword(ctx context.Context, oldPass, newPass string)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc service) ResetPassword(ctx context.Context, token, password string) error {
|
||||
func (svc *Service) ResetPassword(ctx context.Context, token, password string) error {
|
||||
reset, err := svc.ds.FindPassswordResetByToken(token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "looking up reset by token")
|
||||
|
|
@ -242,7 +242,7 @@ func (svc service) ResetPassword(ctx context.Context, token, password string) er
|
|||
|
||||
// prevent setting the same password
|
||||
if err := user.ValidatePassword(password); err == nil {
|
||||
return newInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
return kolide.NewInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
}
|
||||
|
||||
err = svc.setNewPassword(ctx, user, password)
|
||||
|
|
@ -264,10 +264,10 @@ func (svc service) ResetPassword(ctx context.Context, token, password string) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc service) PerformRequiredPasswordReset(ctx context.Context, password string) (*kolide.User, error) {
|
||||
func (svc *Service) PerformRequiredPasswordReset(ctx context.Context, password string) (*kolide.User, error) {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errNoContext
|
||||
return nil, kolide.ErrNoContext
|
||||
}
|
||||
user := vc.User
|
||||
if user.SSOEnabled {
|
||||
|
|
@ -279,7 +279,7 @@ func (svc service) PerformRequiredPasswordReset(ctx context.Context, password st
|
|||
|
||||
// prevent setting the same password
|
||||
if err := user.ValidatePassword(password); err == nil {
|
||||
return nil, newInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
return nil, kolide.NewInvalidArgumentError("new_password", "cannot reuse old password")
|
||||
}
|
||||
|
||||
user.AdminForcedPasswordReset = false
|
||||
|
|
@ -294,7 +294,7 @@ func (svc service) PerformRequiredPasswordReset(ctx context.Context, password st
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) RequirePasswordReset(ctx context.Context, uid uint, require bool) (*kolide.User, error) {
|
||||
func (svc *Service) RequirePasswordReset(ctx context.Context, uid uint, require bool) (*kolide.User, error) {
|
||||
user, err := svc.ds.UserByID(uid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading user by ID")
|
||||
|
|
@ -318,7 +318,7 @@ func (svc service) RequirePasswordReset(ctx context.Context, uid uint, require b
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func (svc service) RequestPasswordReset(ctx context.Context, email string) error {
|
||||
func (svc *Service) RequestPasswordReset(ctx context.Context, email string) error {
|
||||
// Regardless of error, sleep until the request has taken at least 1 second.
|
||||
// This means that any request to this method will take ~1s and frustrate a timing attack.
|
||||
defer func(start time.Time) {
|
||||
|
|
@ -371,7 +371,7 @@ func (svc service) RequestPasswordReset(ctx context.Context, email string) error
|
|||
// saves user in datastore.
|
||||
// doesn't need to be exposed to the transport
|
||||
// the service should expose actions for modifying a user instead
|
||||
func (svc service) saveUser(user *kolide.User) error {
|
||||
func (svc *Service) saveUser(user *kolide.User) error {
|
||||
return svc.ds.SaveUser(user)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,10 +123,9 @@ func TestModifyUserEmailNoPassword(t *testing.T) {
|
|||
}
|
||||
_, err = svc.ModifyUser(ctx, 3, payload)
|
||||
require.NotNil(t, err)
|
||||
invalid, ok := err.(*invalidArgumentError)
|
||||
invalid, ok := err.(*kolide.InvalidArgumentError)
|
||||
require.True(t, ok)
|
||||
require.Len(t, *invalid, 1)
|
||||
assert.Equal(t, "cannot be empty if email is changed", (*invalid)[0].reason)
|
||||
assert.False(t, ms.PendingEmailChangeFuncInvoked)
|
||||
assert.False(t, ms.SaveUserFuncInvoked)
|
||||
|
||||
|
|
@ -169,10 +168,9 @@ func TestModifyAdminUserEmailNoPassword(t *testing.T) {
|
|||
}
|
||||
_, err = svc.ModifyUser(ctx, 3, payload)
|
||||
require.NotNil(t, err)
|
||||
invalid, ok := err.(*invalidArgumentError)
|
||||
invalid, ok := err.(*kolide.InvalidArgumentError)
|
||||
require.True(t, ok)
|
||||
require.Len(t, *invalid, 1)
|
||||
assert.Equal(t, "cannot be empty if email is changed", (*invalid)[0].reason)
|
||||
assert.False(t, ms.PendingEmailChangeFuncInvoked)
|
||||
assert.False(t, ms.SaveUserFuncInvoked)
|
||||
|
||||
|
|
@ -235,7 +233,7 @@ func TestRequestPasswordReset(t *testing.T) {
|
|||
var errEmailFn = func(e kolide.Email) error {
|
||||
return errors.New("test err")
|
||||
}
|
||||
svc := service{
|
||||
svc := &Service{
|
||||
ds: ds,
|
||||
config: config.TestConfig(),
|
||||
}
|
||||
|
|
@ -312,13 +310,13 @@ func TestCreateUserWithInvite(t *testing.T) {
|
|||
Username: ptr.String("admin2"),
|
||||
Password: ptr.String("foobarbaz1234!"),
|
||||
InviteToken: &invites["admin2@example.com"].Token,
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "email", reason: "missing required argument"}},
|
||||
wantErr: kolide.NewInvalidArgumentError("email", "missing required argument"),
|
||||
},
|
||||
{
|
||||
Username: ptr.String("admin2"),
|
||||
Password: ptr.String("foobarbaz1234!"),
|
||||
Email: ptr.String("admin2@example.com"),
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "invite_token", reason: "missing required argument"}},
|
||||
wantErr: kolide.NewInvalidArgumentError("invite_token", "missing required argument"),
|
||||
},
|
||||
{
|
||||
Username: ptr.String("admin2"),
|
||||
|
|
@ -342,7 +340,7 @@ func TestCreateUserWithInvite(t *testing.T) {
|
|||
Email: &invites["expired"].Email,
|
||||
NeedsPasswordReset: ptr.Bool(true),
|
||||
InviteToken: &invites["expired"].Token,
|
||||
wantErr: &invalidArgumentError{{name: "invite_token", reason: "Invite token has expired."}},
|
||||
wantErr: kolide.NewInvalidArgumentError("invite_token", "Invite token has expired."),
|
||||
},
|
||||
{
|
||||
Username: ptr.String("admin3@example.com"),
|
||||
|
|
@ -434,7 +432,7 @@ func TestChangePassword(t *testing.T) {
|
|||
user: users["admin1"],
|
||||
oldPassword: "12345cat!",
|
||||
newPassword: "foobarbaz1234!",
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "new_password", reason: "cannot reuse old password"}},
|
||||
wantErr: kolide.NewInvalidArgumentError("new_password", "cannot reuse old password"),
|
||||
},
|
||||
{ // all good
|
||||
user: users["user1"],
|
||||
|
|
@ -449,14 +447,7 @@ func TestChangePassword(t *testing.T) {
|
|||
},
|
||||
{ // missing old password
|
||||
newPassword: "123cataaa!",
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "old_password", reason: "cannot be empty"}},
|
||||
},
|
||||
{ // missing new password
|
||||
oldPassword: "abcd",
|
||||
wantErr: &invalidArgumentError{
|
||||
{name: "new_password", reason: "cannot be empty"},
|
||||
{name: "new_password", reason: "password does not meet validation requirements"},
|
||||
},
|
||||
wantErr: kolide.NewInvalidArgumentError("old_password", "cannot be empty"),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -501,7 +492,7 @@ func TestResetPassword(t *testing.T) {
|
|||
{ // prevent reuse
|
||||
token: "abcd",
|
||||
newPassword: "123cat!",
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "new_password", reason: "cannot reuse old password"}},
|
||||
wantErr: kolide.NewInvalidArgumentError("new_password", "cannot reuse old password"),
|
||||
},
|
||||
{ // bad token
|
||||
token: "dcbaz",
|
||||
|
|
@ -510,14 +501,7 @@ func TestResetPassword(t *testing.T) {
|
|||
},
|
||||
{ // missing token
|
||||
newPassword: "123cat!",
|
||||
wantErr: &invalidArgumentError{invalidArgument{name: "token", reason: "cannot be empty field"}},
|
||||
},
|
||||
{ // missing password
|
||||
token: "abcd",
|
||||
wantErr: &invalidArgumentError{
|
||||
{name: "new_password", reason: "cannot be empty field"},
|
||||
{name: "new_password", reason: "password does not meet validation requirements"},
|
||||
},
|
||||
wantErr: kolide.NewInvalidArgumentError("token", "cannot be empty field"),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,13 +153,13 @@ func encodeError(ctx context.Context, err error, w http.ResponseWriter) {
|
|||
// Get specific status code if it is available from this error type,
|
||||
// defaulting to HTTP 500
|
||||
status := http.StatusInternalServerError
|
||||
if e, ok := err.(ErrWithStatusCode); ok {
|
||||
if e, ok := err.(kolide.ErrWithStatusCode); ok {
|
||||
status = e.StatusCode()
|
||||
}
|
||||
|
||||
// See header documentation
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
|
||||
if e, ok := err.(ErrWithRetryAfter); ok {
|
||||
if e, ok := err.(kolide.ErrWithRetryAfter); ok {
|
||||
w.Header().Add("Retry-After", strconv.Itoa(e.RetryAfter()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ func (mw validationMiddleware) ModifyAppConfig(ctx context.Context, p kolide.App
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching existing app config in validation")
|
||||
}
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
validateSSOSettings(p, existing, invalid)
|
||||
if invalid.HasErrors() {
|
||||
return nil, invalid
|
||||
|
|
@ -27,7 +27,7 @@ func isSet(val *string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func validateSSOSettings(p kolide.AppConfigPayload, existing *kolide.AppConfig, invalid *invalidArgumentError) {
|
||||
func validateSSOSettings(p kolide.AppConfigPayload, existing *kolide.AppConfig, invalid *kolide.InvalidArgumentError) {
|
||||
if p.SSOSettings != nil && p.SSOSettings.EnableSSO != nil {
|
||||
if *p.SSOSettings.EnableSSO {
|
||||
if !isSet(p.SSOSettings.Metadata) && !isSet(p.SSOSettings.MetadataURL) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestSSONotPresent(t *testing.T) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
var p kolide.AppConfigPayload
|
||||
validateSSOSettings(p, &kolide.AppConfig{}, invalid)
|
||||
assert.False(t, invalid.HasErrors())
|
||||
|
|
@ -17,7 +17,7 @@ func TestSSONotPresent(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNeedFieldsPresent(t *testing.T) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
config := kolide.AppConfig{
|
||||
EnableSSO: true,
|
||||
EntityID: "kolide",
|
||||
|
|
@ -31,7 +31,7 @@ func TestNeedFieldsPresent(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMissingMetadata(t *testing.T) {
|
||||
invalid := invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
config := kolide.AppConfig{
|
||||
EnableSSO: true,
|
||||
EntityID: "kolide",
|
||||
|
|
@ -39,9 +39,8 @@ func TestMissingMetadata(t *testing.T) {
|
|||
IDPName: "onelogin",
|
||||
}
|
||||
p := appConfigPayloadFromAppConfig(&config)
|
||||
validateSSOSettings(*p, &kolide.AppConfig{}, &invalid)
|
||||
validateSSOSettings(*p, &kolide.AppConfig{}, invalid)
|
||||
require.True(t, invalid.HasErrors())
|
||||
require.Len(t, invalid, 1)
|
||||
assert.Equal(t, "metadata", invalid[0].name)
|
||||
assert.Equal(t, "either metadata or metadata_url must be defined", invalid[0].reason)
|
||||
assert.Contains(t, invalid.Error(), "metadata")
|
||||
assert.Contains(t, invalid.Error(), "either metadata or metadata_url must be defined")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func (mw validationMiddleware) InviteNewUser(ctx context.Context, payload kolide.InvitePayload) (*kolide.Invite, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if payload.Email == nil {
|
||||
invalid.Append("email", "missing required argument")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func (mw validationMiddleware) NewAppConfig(ctx context.Context, payload kolide.AppConfigPayload) (*kolide.AppConfig, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
var serverURLString string
|
||||
if payload.ServerSettings == nil {
|
||||
invalid.Append("kolide_server_url", "missing required argument")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func (mw validationMiddleware) CreateUserWithInvite(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if p.Username == nil {
|
||||
invalid.Append("username", "missing required argument")
|
||||
} else {
|
||||
|
|
@ -56,7 +56,7 @@ func (mw validationMiddleware) CreateUserWithInvite(ctx context.Context, p kolid
|
|||
}
|
||||
|
||||
func (mw validationMiddleware) CreateUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if p.Username == nil {
|
||||
invalid.Append("username", "missing required argument username")
|
||||
} else {
|
||||
|
|
@ -96,7 +96,7 @@ func (mw validationMiddleware) CreateUser(ctx context.Context, p kolide.UserPayl
|
|||
}
|
||||
|
||||
func (mw validationMiddleware) ModifyUser(ctx context.Context, userID uint, p kolide.UserPayload) (*kolide.User, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if p.Username != nil {
|
||||
if *p.Username == "" {
|
||||
invalid.Append("username", "cannot be empty")
|
||||
|
|
@ -128,7 +128,7 @@ func (mw validationMiddleware) ModifyUser(ctx context.Context, userID uint, p ko
|
|||
return mw.Service.ModifyUser(ctx, userID, p)
|
||||
}
|
||||
|
||||
func passwordRequiredForEmailChange(ctx context.Context, uid uint, invalid *invalidArgumentError) bool {
|
||||
func passwordRequiredForEmailChange(ctx context.Context, uid uint, invalid *kolide.InvalidArgumentError) bool {
|
||||
vc, ok := viewer.FromContext(ctx)
|
||||
if !ok {
|
||||
invalid.Append("viewer", "not present")
|
||||
|
|
@ -149,7 +149,7 @@ func passwordRequiredForEmailChange(ctx context.Context, uid uint, invalid *inva
|
|||
}
|
||||
|
||||
func (mw validationMiddleware) ChangePassword(ctx context.Context, oldPass, newPass string) error {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if oldPass == "" {
|
||||
invalid.Append("old_password", "cannot be empty")
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ func (mw validationMiddleware) ChangePassword(ctx context.Context, oldPass, newP
|
|||
}
|
||||
|
||||
func (mw validationMiddleware) ResetPassword(ctx context.Context, token, password string) error {
|
||||
invalid := &invalidArgumentError{}
|
||||
invalid := &kolide.InvalidArgumentError{}
|
||||
if token == "" {
|
||||
invalid.Append("token", "cannot be empty field")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
|
@ -69,7 +70,10 @@ func ParseMetadata(metadata string) (*Metadata, error) {
|
|||
// IDP via a remote URL. metadataURL is the location where the metadata is located
|
||||
// and timeout defines how long to wait to get a response form the metadata
|
||||
// server.
|
||||
func GetMetadata(metadataURL string, client *http.Client) (*Metadata, error) {
|
||||
func GetMetadata(metadataURL string) (*Metadata, error) {
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
request, err := http.NewRequest(http.MethodGet, metadataURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -62,10 +61,7 @@ func TestGetMetadata(t *testing.T) {
|
|||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(metadata))
|
||||
}))
|
||||
client := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
settings, err := GetMetadata(ts.URL, client)
|
||||
settings, err := GetMetadata(ts.URL)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "http://www.okta.com/exka4zkf6dxm8pF220h7", settings.EntityID)
|
||||
assert.Len(t, settings.IDPSSODescriptor.NameIDFormats, 2)
|
||||
|
|
|
|||
Loading…
Reference in a new issue