Skip to content

Commit

Permalink
Changed app audit --approve cli command to just app approve with --dr…
Browse files Browse the repository at this point in the history
…y-run option
  • Loading branch information
akclace committed Dec 20, 2023
1 parent 0d8b44d commit 47afb7b
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 82 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ To create an app, run the Clace client
$HOME/clace app create --is_dev /disk_usage $HOME/clace_source/clace/examples/disk_usage/
```

To audit and approve the app's security policies, run
To approve the app's security policies, run

```shell
$HOME/clace app audit --approve /disk_usage
$HOME/clace app approve /disk_usage
```

This will create an app at /disk_usage with the example disk_usage app. The disk_usage app provides a web interface for the [du command](https://man7.org/linux/man-pages/man1/du.1.html), allowing the user to explore the subfolders which are consuming most disk space.
Expand Down
67 changes: 42 additions & 25 deletions cmd/clace/app_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import (
"github.com/urfave/cli/v2/altsrc"
)

const (
DRY_RUN_FLAG = "dry-run"
DRY_RUN_ARG = "dryRun"
DRY_RUN_MESSAGE = "\ndry-run mode, changes have NOT been committed.\n"
)

func initAppCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
return &cli.Command{
Name: "app",
Expand All @@ -23,13 +29,17 @@ func initAppCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *c
appCreateCommand(commonFlags, clientConfig),
appListCommand(commonFlags, clientConfig),
appDeleteCommand(commonFlags, clientConfig),
appAuditCommand(commonFlags, clientConfig),
appApproveCommand(commonFlags, clientConfig),
appReloadCommand(commonFlags, clientConfig),
appPromoteCommand(commonFlags, clientConfig),
},
}
}

func dryRunFlag() *cli.BoolFlag {
return newBoolFlag(DRY_RUN_FLAG, "", "Verify command but don't commit any changes", false)
}

func appCreateCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
flags := make([]cli.Flag, 0, len(commonFlags)+2)
flags = append(flags, commonFlags...)
Expand All @@ -39,6 +49,7 @@ func appCreateCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
flags = append(flags, newStringFlag("branch", "b", "The branch to checkout if using git source", "main"))
flags = append(flags, newStringFlag("commit", "c", "The commit SHA to checkout if using git source. This takes precedence over branch", ""))
flags = append(flags, newStringFlag("git-auth", "g", "The name of the git_auth entry to use", ""))
flags = append(flags, dryRunFlag())

return &cli.Command{
Name: "create",
Expand All @@ -65,6 +76,7 @@ Create app for specified domain, no auth : clace app create --approve --auth-typ
values := url.Values{}
values.Add("appPath", cCtx.Args().Get(0))
values.Add("approve", strconv.FormatBool(cCtx.Bool("approve")))
values.Add(DRY_RUN_ARG, strconv.FormatBool(cCtx.Bool(DRY_RUN_FLAG)))

body := utils.CreateAppRequest{
SourceUrl: cCtx.Args().Get(1),
Expand All @@ -74,22 +86,22 @@ Create app for specified domain, no auth : clace app create --approve --auth-typ
GitCommit: cCtx.String("commit"),
GitAuthName: cCtx.String("git-auth"),
}
var auditResult utils.AuditResult
err := client.Post("/_clace/app", values, body, &auditResult)
var approveResult utils.ApproveResult
err := client.Post("/_clace/app", values, body, &approveResult)
if err != nil {
return err
}
fmt.Printf("App audit results %s : %s\n", cCtx.Args().First(), auditResult.Id)
fmt.Printf("App audit results %s : %s\n", cCtx.Args().First(), approveResult.Id)
fmt.Printf(" Plugins :\n")
for _, load := range auditResult.NewLoads {
for _, load := range approveResult.NewLoads {
fmt.Printf(" %s\n", load)
}
fmt.Printf(" Permissions:\n")
for _, perm := range auditResult.NewPermissions {
for _, perm := range approveResult.NewPermissions {
fmt.Printf(" %s.%s %s\n", perm.Plugin, perm.Method, perm.Arguments)
}

if auditResult.NeedsApproval {
if approveResult.NeedsApproval {
if cCtx.Bool("approve") {
fmt.Print("App created. Permissions have been approved\n")
} else {
Expand Down Expand Up @@ -206,6 +218,7 @@ func appType(app utils.AppResponse) string {
func appDeleteCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
flags := make([]cli.Flag, 0, len(commonFlags)+2)
flags = append(flags, commonFlags...)
flags = append(flags, dryRunFlag())

return &cli.Command{
Name: "delete",
Expand All @@ -221,6 +234,7 @@ func appDeleteCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
client := utils.NewHttpClient(clientConfig.ServerUri, clientConfig.AdminUser, clientConfig.AdminPassword, clientConfig.SkipCertCheck)
values := url.Values{}
values.Add("pathSpec", cCtx.Args().Get(0))
values.Add(DRY_RUN_ARG, strconv.FormatBool(cCtx.Bool(DRY_RUN_FLAG)))

err := client.Delete("/_clace/app", values)
if err != nil {
Expand All @@ -232,14 +246,14 @@ func appDeleteCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
}
}

func appAuditCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
func appApproveCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
flags := make([]cli.Flag, 0, len(commonFlags)+2)
flags = append(flags, commonFlags...)
flags = append(flags, newBoolFlag("approve", "a", "Approve the app permissions", false))
flags = append(flags, dryRunFlag())

return &cli.Command{
Name: "audit",
Usage: "Audit app permissions",
Name: "approve",
Usage: "Approve app permissions",
Flags: flags,
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewTomlSourceFromFlagFunc(configFileFlagName)),
ArgsUsage: "<pathSpec>",
Expand All @@ -251,35 +265,35 @@ func appAuditCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *
client := utils.NewHttpClient(clientConfig.ServerUri, clientConfig.AdminUser, clientConfig.AdminPassword, clientConfig.SkipCertCheck)
values := url.Values{}
values.Add("pathSpec", cCtx.Args().Get(0))
values.Add("approve", strconv.FormatBool(cCtx.Bool("approve")))
values.Add(DRY_RUN_ARG, strconv.FormatBool(cCtx.Bool(DRY_RUN_FLAG)))

var auditResponse utils.AppAuditResponse
err := client.Post("/_clace/audit", values, nil, &auditResponse)
var approveResponse utils.AppApproveResponse
err := client.Post("/_clace/approve", values, nil, &approveResponse)
if err != nil {
return err
}
for _, auditResult := range auditResponse.AuditResults {
fmt.Printf("App audit: %s\n", auditResult.AppPathDomain)
for _, approveResult := range approveResponse.ApproveResults {
fmt.Printf("App audit: %s\n", approveResult.AppPathDomain)
fmt.Printf(" Plugins :\n")
for _, load := range auditResult.NewLoads {
for _, load := range approveResult.NewLoads {
fmt.Printf(" %s\n", load)
}
fmt.Printf(" Permissions:\n")
for _, perm := range auditResult.NewPermissions {
for _, perm := range approveResult.NewPermissions {
fmt.Printf(" %s.%s %s\n", perm.Plugin, perm.Method, perm.Arguments)
}

if auditResult.NeedsApproval {
if cCtx.Bool("approve") {
fmt.Printf("App permissions have been approved.\n")
} else {
fmt.Printf("App permissions need to be approved...\n")
}
if approveResult.NeedsApproval {
fmt.Printf("App permissions have been approved.\n")
} else {
fmt.Printf("App permissions are current, no approval required.\n")
}
}

if cCtx.Bool(DRY_RUN_FLAG) {
fmt.Print(DRY_RUN_MESSAGE)
}

return nil
},
}
Expand All @@ -293,7 +307,7 @@ func appReloadCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
flags = append(flags, newStringFlag("branch", "b", "The branch to checkout if using git source", ""))
flags = append(flags, newStringFlag("commit", "c", "The commit SHA to checkout if using git source. This takes precedence over branch", ""))
flags = append(flags, newStringFlag("git-auth", "g", "The name of the git_auth entry to use", ""))
flags = append(flags, newBoolFlag("dry-run", "n", "Whether to run in dry run (check only) mode", false))
flags = append(flags, dryRunFlag())

return &cli.Command{
Name: "reload",
Expand All @@ -314,6 +328,7 @@ func appReloadCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
values.Add("branch", cCtx.String("branch"))
values.Add("commit", cCtx.String("commit"))
values.Add("gitAuth", cCtx.String("git-auth"))
values.Add(DRY_RUN_ARG, strconv.FormatBool(cCtx.Bool(DRY_RUN_FLAG)))

var response map[string]any
err := client.Post("/_clace/reload", values, nil, &response)
Expand All @@ -329,6 +344,7 @@ func appReloadCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
func appPromoteCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig) *cli.Command {
flags := make([]cli.Flag, 0, len(commonFlags)+2)
flags = append(flags, commonFlags...)
flags = append(flags, dryRunFlag())

return &cli.Command{
Name: "promote",
Expand All @@ -344,6 +360,7 @@ func appPromoteCommand(commonFlags []cli.Flag, clientConfig *utils.ClientConfig)
client := utils.NewHttpClient(clientConfig.ServerUri, clientConfig.AdminUser, clientConfig.AdminPassword, clientConfig.SkipCertCheck)
values := url.Values{}
values.Add("pathSpec", cCtx.Args().First())
values.Add(DRY_RUN_ARG, strconv.FormatBool(cCtx.Bool(DRY_RUN_FLAG)))

var response map[string]any
err := client.Post("/_clace/promote", values, nil, &response)
Expand Down
10 changes: 5 additions & 5 deletions internal/app/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"go.starlark.net/starlarkstruct"
)

func (a *App) Audit() (*utils.AuditResult, error) {
func (a *App) Approve() (*utils.ApproveResult, error) {
buf, err := a.sourceFS.ReadFile(util.APP_FILE_NAME)
if err != nil {
return nil, fmt.Errorf("error reading %s file: %w", util.APP_FILE_NAME, err)
Expand Down Expand Up @@ -85,10 +85,10 @@ func (a *App) Audit() (*utils.AuditResult, error) {
return nil, fmt.Errorf("source init failed %v", err)
}

return a.createAuditResponse(loads, globals)
return a.createApproveResponse(loads, globals)
}

func needsApproval(a *utils.AuditResult) bool {
func needsApproval(a *utils.ApproveResult) bool {
if !slices.Equal(a.NewLoads, a.ApprovedLoads) {
return true
}
Expand All @@ -107,7 +107,7 @@ func needsApproval(a *utils.AuditResult) bool {
return !slices.EqualFunc(a.NewPermissions, a.ApprovedPermissions, permEquals)
}

func (a *App) createAuditResponse(loads []string, globals starlark.StringDict) (*utils.AuditResult, error) {
func (a *App) createApproveResponse(loads []string, globals starlark.StringDict) (*utils.ApproveResult, error) {
// the App entry should not get updated during the audit call, since there
// can be audit calls when the app is running.
appDef, err := verifyConfig(globals)
Expand All @@ -116,7 +116,7 @@ func (a *App) createAuditResponse(loads []string, globals starlark.StringDict) (
}

perms := []utils.Permission{}
results := utils.AuditResult{
results := utils.ApproveResult{
AppPathDomain: a.AppEntry.AppPathDomain(),
Id: a.Id,
NewLoads: loads,
Expand Down
47 changes: 28 additions & 19 deletions internal/server/app_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func normalizePath(inp string) string {
return inp
}

func (s *Server) CreateApp(ctx context.Context, appPath string, approve bool, appRequest utils.CreateAppRequest) (*utils.AuditResult, error) {
func (s *Server) CreateApp(ctx context.Context, appPath string, approve bool, appRequest utils.CreateAppRequest) (*utils.ApproveResult, error) {
appPathDomain, err := parseAppPath(appPath)
if err != nil {
return nil, err
Expand Down Expand Up @@ -97,7 +97,7 @@ func (s *Server) CreateApp(ctx context.Context, appPath string, approve bool, ap
return auditResult, nil
}

func (s *Server) createApp(ctx context.Context, appEntry *utils.AppEntry, approve bool, branch, commit, gitAuth string) (*utils.AuditResult, error) {
func (s *Server) createApp(ctx context.Context, appEntry *utils.AppEntry, approve bool, branch, commit, gitAuth string) (*utils.ApproveResult, error) {
if isGit(appEntry.SourceUrl) {
if appEntry.IsDev {
return nil, fmt.Errorf("cannot create dev mode app from git source. For dev mode, manually checkout the git repo and create app from the local path")
Expand Down Expand Up @@ -378,17 +378,13 @@ func (s *Server) CheckAppValid(domain, matchPath string) (string, error) {
return matchedApp, nil
}

func (s *Server) auditApp(ctx context.Context, tx metadata.Transaction, app *app.App, approve bool) (*utils.AuditResult, error) {
auditResult, err := app.Audit()
func (s *Server) auditApp(ctx context.Context, tx metadata.Transaction, app *app.App, approve bool) (*utils.ApproveResult, error) {
auditResult, err := app.Approve()
if err != nil {
return nil, err
}

if approve {
if err != nil {
return nil, err
}

app.AppEntry.Metadata.Loads = auditResult.NewLoads
app.AppEntry.Metadata.Permissions = auditResult.NewPermissions
if err := s.db.UpdateAppMetadata(ctx, tx, app.AppEntry); err != nil {
Expand All @@ -400,44 +396,57 @@ func (s *Server) auditApp(ctx context.Context, tx metadata.Transaction, app *app
return auditResult, nil
}

func (s *Server) AuditApps(ctx context.Context, pathSpec string, approve bool) ([]utils.AuditResult, error) {
func (s *Server) ApproveApps(ctx context.Context, pathSpec string, dryRun bool) ([]utils.ApproveResult, error) {
tx, err := s.db.BeginTransaction(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()

ret, err := s.AuditAppsTx(ctx, tx, pathSpec, approve)
ret, apps, err := s.AuditAppsTx(ctx, tx, pathSpec)
if err != nil {
return nil, err
}

if err = tx.Commit(); err != nil {
if dryRun {
return ret, nil
}

if err := tx.Commit(); err != nil {
return nil, err
}

// Update the in memory cache
s.apps.UpdateApps(apps)
return ret, nil
}

func (s *Server) AuditAppsTx(ctx context.Context, tx metadata.Transaction, pathSpec string, approve bool) ([]utils.AuditResult, error) {
func (s *Server) AuditAppsTx(ctx context.Context, tx metadata.Transaction, pathSpec string) ([]utils.ApproveResult, []*app.App, error) {
filteredApps, err := s.FilterApps(pathSpec, false)
if err != nil {
return nil, utils.CreateRequestError(err.Error(), http.StatusBadRequest)
return nil, nil, utils.CreateRequestError(err.Error(), http.StatusBadRequest)
}

results := make([]utils.AuditResult, 0, len(filteredApps))
results := make([]utils.ApproveResult, 0, len(filteredApps))
apps := make([]*app.App, 0, len(filteredApps))
for _, appInfo := range filteredApps {
app, err := s.GetApp(appInfo.AppPathDomain, false)
appEntry, err := s.GetAppEntry(ctx, tx, appInfo.AppPathDomain)
if err != nil {
return nil, err
return nil, nil, err
}
result, err := s.auditApp(ctx, tx, app, approve)
app, err := s.setupApp(appEntry, tx)
if err != nil {
return nil, err
return nil, nil, err
}
apps = append(apps, app)
result, err := s.auditApp(ctx, tx, app, true)
if err != nil {
return nil, nil, err
}
results = append(results, *result)
}

return results, nil
return results, apps, nil
}

func parseGithubUrl(sourceUrl string) (repo string, folder string, err error) {
Expand Down
Loading

0 comments on commit 47afb7b

Please sign in to comment.