Skip to content

Commit

Permalink
feat: rules commands (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
fredmaggiowski authored Nov 21, 2024
1 parent e57b9d7 commit a839e96
Show file tree
Hide file tree
Showing 11 changed files with 878 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:
- examples/**

env:
GORELEASER_VERSION: v2.4.1
GORELEASER_VERSION: v2.4.4
jobs:
lint:
name: Lint Code
Expand Down
57 changes: 57 additions & 0 deletions docs/30_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,63 @@ Available flags for the command:
- `--context`, to specify a different context from the currently selected one
- `--company-id`, to set the ID of the desired Company

### rules

Rules command helps you manage different rules for configuration update for the whole Company or specific Projects.

:::tip
This feature is currently in closed preview and may be subject to breaking changes, reach out to your Mia-Platorm referent
if you are interested in use it.
:::

#### list

List available rules for the Company or for a specific Project.

Usage:

```sh
miactl company rules list [flags]
```

Available flags for the command:

- `--company-id`, the id of the Company
- `--project-id`, the id of the Project (if provided the command will print avilable rules for the project,
together with the rules inherited from the Company)

#### update

Helps you update rules for a Company or for a specific Project

Usage:

```sh
miactl company rules update [flags]
```

Available flags for the command:

- `--company-id`, the id of the Company
- `--project-id`, the id of the Project (if provided the command will update the rules for the specified Project only)
- `-f`, path to the file where the rules are saved

<details>
<summary>File example</summary>

```json
[
{
"roleIds": ["developer"],
"disallowedRuleSet": [
{"ruleId": "endpoint.security.edit"}
]
}
]
```

</details>

## project

This command allows you to manage `miactl` Projects.
Expand Down
4 changes: 4 additions & 0 deletions internal/cmd/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ func CompanyCmd(options *clioptions.CLIOptions) *cobra.Command {
company.IAMCmd(options),
)

cmd.AddCommand(
company.RulesCmd(options),
)

return cmd
}
44 changes: 44 additions & 0 deletions internal/cmd/company/rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright Mia srl
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package company

import (
"github.com/mia-platform/miactl/internal/clioptions"
"github.com/mia-platform/miactl/internal/cmd/company/rules"
"github.com/spf13/cobra"
)

func RulesCmd(o *clioptions.CLIOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "rules",
Short: "Manage Company rules",
Long: ``,
}

// add cmd flags
flags := cmd.PersistentFlags()
o.AddConnectionFlags(flags)
o.AddContextFlags(flags)
o.AddCompanyFlags(flags)
o.AddProjectFlags(flags)

cmd.AddCommand(
rules.ListCmd(o),
rules.UpdateRules(o),
)

return cmd
}
177 changes: 177 additions & 0 deletions internal/cmd/company/rules/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright Mia srl
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package rules

import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/mia-platform/miactl/internal/client"
"github.com/mia-platform/miactl/internal/resources"
rulesentities "github.com/mia-platform/miactl/internal/resources/rules"
)

const (
tenantsAPIPrefix = "/api/backend/tenants/"
projectsAPIPrefix = "/api/backend/projects/"
getProjectAPIFmt = projectsAPIPrefix + "%s"
patchTenantRulesFmt = tenantsAPIPrefix + "%s/rules"
patchProjectRulesFmt = projectsAPIPrefix + "%s/rules"
)

type IRulesClient interface {
ListTenantRules(ctx context.Context, companyID string) ([]*rulesentities.SaveChangesRules, error)
ListProjectRules(ctx context.Context, projectID string) ([]*rulesentities.ProjectSaveChangesRules, error)
UpdateTenantRules(ctx context.Context, companyID string, rules []*rulesentities.SaveChangesRules) error
UpdateProjectRules(ctx context.Context, projectID string, rules []*rulesentities.SaveChangesRules) error
}

type rulesClient struct {
c *client.APIClient
}

func New(c *client.APIClient) IRulesClient {
return &rulesClient{c: c}
}

func (e *rulesClient) ListTenantRules(ctx context.Context, companyID string) ([]*rulesentities.SaveChangesRules, error) {
request := e.c.Get().APIPath(tenantsAPIPrefix)
request.SetParam("search", companyID)

resp, err := request.Do(ctx)
if err != nil {
return nil, fmt.Errorf("error executing request: %w", err)
}
if err := e.assertSuccessResponse(resp); err != nil {
return nil, err
}

var tenants []resources.Company
if err := resp.ParseResponse(&tenants); err != nil {
return nil, fmt.Errorf("error parsing response body: %w", err)
}
if len(tenants) == 0 {
return nil, fmt.Errorf("Company %s not found", companyID)
}
var tenant *resources.Company
for _, possible := range tenants {
if possible.TenantID == companyID {
tenant = &possible
break
}
}
if tenant == nil {
return nil, fmt.Errorf("Company %s not found", companyID)
}
if len(tenant.ConfigurationManagement.SaveChangesRules) == 0 {
return []*rulesentities.SaveChangesRules{}, nil
}

return tenant.ConfigurationManagement.SaveChangesRules, nil
}

func (e *rulesClient) ListProjectRules(ctx context.Context, projectID string) ([]*rulesentities.ProjectSaveChangesRules, error) {
request := e.c.Get().APIPath(fmt.Sprintf(getProjectAPIFmt, projectID))
request.SetParam("withTenant", "true")

resp, err := request.Do(ctx)
if err != nil {
return nil, fmt.Errorf("error executing request: %w", err)
}
if err := e.assertSuccessResponse(resp); err != nil {
return nil, err
}

var project resources.Project
if err := resp.ParseResponse(&project); err != nil {
return nil, fmt.Errorf("error parsing response body: %w", err)
}

if len(project.ConfigurationManagement.SaveChangesRules) == 0 {
return []*rulesentities.ProjectSaveChangesRules{}, nil
}

return project.ConfigurationManagement.SaveChangesRules, nil
}

type UpdateRequestBody struct {
ConfigurationManagement *resources.ConfigurationManagement `json:"configurationManagement"`
}

func (e *rulesClient) UpdateTenantRules(ctx context.Context, companyID string, rules []*rulesentities.SaveChangesRules) error {
requestBody := UpdateRequestBody{
ConfigurationManagement: &resources.ConfigurationManagement{
SaveChangesRules: rules,
},
}
bodyData, err := json.Marshal(requestBody)
if err != nil {
return err
}

request := e.c.Patch().
APIPath(
fmt.Sprintf(patchTenantRulesFmt, companyID),
).
Body(bodyData)

resp, err := request.Do(ctx)
if err != nil {
return fmt.Errorf("error executing request: %w", err)
}
if err := e.assertSuccessResponse(resp); err != nil {
return err
}

return nil
}

func (e *rulesClient) UpdateProjectRules(ctx context.Context, projectID string, rules []*rulesentities.SaveChangesRules) error {
requestBody := UpdateRequestBody{
ConfigurationManagement: &resources.ConfigurationManagement{
SaveChangesRules: rules,
},
}
bodyData, err := json.Marshal(requestBody)
if err != nil {
return err
}

request := e.c.Patch().
APIPath(
fmt.Sprintf(patchProjectRulesFmt, projectID),
).
Body(bodyData)

resp, err := request.Do(ctx)
if err != nil {
return fmt.Errorf("error executing request: %w", err)
}
if err := e.assertSuccessResponse(resp); err != nil {
return err
}

return nil
}

func (e *rulesClient) assertSuccessResponse(resp *client.Response) error {
if resp.StatusCode() >= http.StatusBadRequest {
return resp.Error()
}
return nil
}
Loading

0 comments on commit a839e96

Please sign in to comment.