Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rules commands #228

Merged
merged 6 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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