Skip to content

Commit

Permalink
refactor: frontier provider for the org, group and project access
Browse files Browse the repository at this point in the history
  • Loading branch information
Chief-Rishab committed Nov 15, 2023
1 parent afc0db1 commit db13c4f
Show file tree
Hide file tree
Showing 8 changed files with 2,137 additions and 2,098 deletions.
2 changes: 1 addition & 1 deletion docs/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ For more information on using the Guardian CLI, see the [CLI Reference](./refere

#### HTTPS API

You can get hands on appeal creation, approval updatation, access revocation and much more by using the Guardian HTTPS API, which lets you issue HTTPS requests directly to the service. When you use the HTTPS API, you must include the username in the request header, which will be used by [Shield](https://guardian.vercel.app/shield/) for authorization. For more information, see the [API Reference](/reference/api.md) page.
You can get hands on appeal creation, approval updatation, access revocation and much more by using the Guardian HTTPS API, which lets you issue HTTPS requests directly to the service. When you use the HTTPS API, you must include the username in the request header, which will be used by [Frontier](https://guardian.vercel.app/frontier/) for authorization. For more information, see the [API Reference](/reference/api.md) page.

## Where to go from here

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ requirements:

| Field | Type | Description | Required |
| :----- | :---- | :------ | :------ |
| `provider` | `string` | Identity manager type. Supported types are `http` and `shield` | YES |
| `provider` | `string` | Identity manager type. Supported types are `http` and `frontier` | YES |
| `config` | `object`| Client configuration according to the `provider` type | YES |
| `schema` | `map<string,string>` | User (appeal creator) profile details schema to be shown in the `creator` field in an appeal | NO |

Expand Down
14 changes: 11 additions & 3 deletions internal/server/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ GRPC:
TIMEOUT_IN_SECONDS: 5
MAX_CALL_RECV_MSG_SIZE: 33554432
MAX_CALL_SEND_MSG_SIZE: 33554432
ENCRYPTION_SECRET_KEY:
ENCRYPTION_SECRET_KEY: "<secret-key>"
AUTHENTICATED_USER_HEADER_KEY: X-Auth-Email
AUDIT_LOG_TRACE_ID_HEADER_KEY: X-Trace-Id
LOG:
LEVEL: info
DB:
Expand All @@ -27,11 +28,18 @@ DB:
SSLMODE: disable
NOTIFIER:
PROVIDER: slack
ACCESS_TOKEN:
ACCESS_TOKEN: <slack-access-token>
WORKSPACES:
- WORKSPACE: raystack
ACCESS_TOKEN:
ACCESS_TOKEN: <slack-access-token>
CRITERIA: "email contains '@raystack.io'"
MESSAGES:
EXPIRATION_REMINDER: "Your access {{.account_id}} to {{.resource_name}} with role {{.role}} will expire at {{.expiration_date}}. Extend the access if it's still needed"
APPEAL_APPROVED: "Your appeal to {{.resource_name}} with role {{.role}} has been approved"
APPEAL_REJECTED: "Your appeal to {{.resource_name}} with role {{.role}} has been rejected"
ACCESS_REVOKED: "Your access to {{.resource_name}}} with role {{.role}} has been revoked"
APPROVER_NOTIFICATION: "You have an appeal created by {{.requestor}} requesting access to {{.resource_name}} with role {{.role}}. Appeal ID: {{.appeal_id}}"
OTHERS_APPEAL_APPROVED: "Your appeal to {{.resource_name}} with role {{.role}} created by {{.requestor}} has been approved"
JOBS:
FETCH_RESOURCES:
ENABLED: true
Expand Down
147 changes: 85 additions & 62 deletions plugins/providers/frontier/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,29 @@ import (
)

const (
groupsEndpoint = "/v1beta1/groups"
projectsEndpoint = "/v1beta1/projects"
groupsEndpoint = "/v1beta1/organizations/%s/groups"
projectsEndpoint = "/v1beta1/organizations/%s/projects"
organizationEndpoint = "/v1beta1/organizations"
selfUserEndpoint = "/v1beta1/users/self"
createPolicyEndpoint = "/v1beta1/policies"

groupsConst = "groups"
projectsConst = "projects"
organizationsConst = "organizations"
usersConst = "users"
userConst = "user"
policiesConst = "policies"
)

type successAccess interface{}

Check failure on line 34 in plugins/providers/frontier/client.go

View workflow job for this annotation

GitHub Actions / lint

type `successAccess` is unused (unused)

Check failure on line 34 in plugins/providers/frontier/client.go

View workflow job for this annotation

GitHub Actions / lint

type `successAccess` is unused (unused)

type Policy struct {
ID string `json:"id"`
}

type Client interface {
GetTeams() ([]*Team, error)
GetProjects() ([]*Project, error)
GetTeams(orgID string) ([]*Team, error)
GetProjects(orgID string) ([]*Project, error)
GetOrganizations() ([]*Organization, error)
GrantTeamAccess(team *Team, userId string, role string) error
RevokeTeamAccess(team *Team, userId string, role string) error
Expand Down Expand Up @@ -77,7 +83,7 @@ func NewClient(config *ClientConfig, logger log.Logger) (*client, error) {

httpClient := config.HTTPClient
if httpClient == nil {
httpClient = tracing.NewHttpClient("ShieldHttpClient")
httpClient = tracing.NewHttpClient("FrontierHttpClient")
}

c := &client{
Expand Down Expand Up @@ -144,7 +150,8 @@ func (c *client) GetAdminsOfGivenResourceType(id string, resourceTypeEndPoint st
return userEmails, err
}

func (c *client) GetTeams() ([]*Team, error) {
func (c *client) GetTeams(orgID string) ([]*Team, error) {
groupsEndpoint := fmt.Sprintf(groupsEndpoint, orgID)
req, err := c.newRequest(http.MethodGet, groupsEndpoint, nil, "")
if err != nil {
return nil, err
Expand Down Expand Up @@ -173,7 +180,8 @@ func (c *client) GetTeams() ([]*Team, error) {
return teams, err
}

func (c *client) GetProjects() ([]*Project, error) {
func (c *client) GetProjects(orgID string) ([]*Project, error) {
projectsEndpoint := fmt.Sprintf(projectsEndpoint, orgID)
req, err := c.newRequest(http.MethodGet, projectsEndpoint, nil, "")
if err != nil {
return nil, err
Expand Down Expand Up @@ -233,154 +241,169 @@ func (c *client) GetOrganizations() ([]*Organization, error) {
}

func (c *client) GrantTeamAccess(resource *Team, userId string, role string) error {
body := make(map[string][]string)
body["userIds"] = append(body["userIds"], userId)
body := make(map[string]string)
body["principal"] = fmt.Sprintf("%s:%s", "app/user", userId)
body["resource"] = fmt.Sprintf("%s:%s", "app/group", resource.ID)
body["roleId"] = role

endPoint := path.Join(groupsEndpoint, "/", resource.ID, "/", role)
req, err := c.newRequest(http.MethodPost, endPoint, body, "")
req, err := c.newRequest(http.MethodPost, createPolicyEndpoint, body, "")
if err != nil {
return err
}

var users []*User
var response interface{}
if _, err := c.do(req, &response); err != nil {
c.logger.Error("Failed to grant access to the user", "Users", userId, req.URL)
return err
}

if v, ok := response.(map[string]interface{}); ok && v[usersConst] != nil {
err = mapstructure.Decode(v[usersConst], &users)
if err != nil {
return err
}
}

c.logger.Info("Team access to the user,", "total users", len(users), req.URL)

c.logger.Info("Team access to the user", "Users", userId, req.URL)
return nil
}

func (c *client) GrantProjectAccess(resource *Project, userId string, role string) error {
body := make(map[string][]string)
body["userIds"] = append(body["userIds"], userId)
body := make(map[string]string)
body["principal"] = fmt.Sprintf("%s:%s", "app/user", userId)
body["resource"] = fmt.Sprintf("%s:%s", "app/project", resource.ID)
body["roleId"] = role

endPoint := path.Join(projectsEndpoint, "/", resource.ID, "/", role)
req, err := c.newRequest(http.MethodPost, endPoint, body, "")
req, err := c.newRequest(http.MethodPost, createPolicyEndpoint, body, "")
if err != nil {
return err
}

var users []*User
var response interface{}
if _, err := c.do(req, &response); err != nil {
c.logger.Error("Failed to grant access to the user", "Users", userId, req.URL)
return err
}

if v, ok := response.(map[string]interface{}); ok && v[usersConst] != nil {
err = mapstructure.Decode(v[usersConst], &users)
if err != nil {
return err
}
}

c.logger.Info("Project access to the user,", "total users", len(users), req.URL)
c.logger.Info("Project access to the user", "Users", userId, req.URL)
return nil
}

func (c *client) GrantOrganizationAccess(resource *Organization, userId string, role string) error {
body := make(map[string][]string)
body["userIds"] = append(body["userIds"], userId)

endPoint := path.Join(organizationEndpoint, "/", resource.ID, "/", role)
req, err := c.newRequest(http.MethodPost, endPoint, body, "")
func (c *client) GrantOrganizationAccess(resource *Organization, userId string, roleId string) error {
body := make(map[string]string)
body["roleId"] = roleId
body["resource"] = fmt.Sprintf("%s:%s", "app/organization", resource.ID)
body["principal"] = fmt.Sprintf("%s:%s", "app/user", userId)

req, err := c.newRequest(http.MethodPost, createPolicyEndpoint, body, "")
if err != nil {
return err
}

var users []*User
var response interface{}
if _, err := c.do(req, &response); err != nil {
c.logger.Error("Failed to grant access to the user", "Users", userId, req.URL)
return err
}

if v, ok := response.(map[string]interface{}); ok && v[usersConst] != nil {
err = mapstructure.Decode(v[usersConst], &users)
if err != nil {
return err
}
}

c.logger.Info("Organization access to the user,", "total users", len(users), req.URL)
c.logger.Info("Organization access to the user,", "Users", userId, req.URL)
return nil
}

func (c *client) RevokeTeamAccess(resource *Team, userId string, role string) error {
endPoint := path.Join(groupsEndpoint, "/", resource.ID, "/", role, "/", userId)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
endpoint := createPolicyEndpoint + "?groupId=" + resource.ID + "&userId=" + userId + "&roleId=" + role
req, err := c.newRequest(http.MethodGet, endpoint, "", "")
if err != nil {
return err
}

var success successAccess
var policies []*Policy
var response interface{}
if _, err := c.do(req, &response); err != nil {
return err
}

if v, ok := response.(map[string]interface{}); ok && v != nil {
err = mapstructure.Decode(v, &success)
err = mapstructure.Decode(v[policiesConst], &policies)
if err != nil {
return err
}
}

for _, policy := range policies {
endPoint := path.Join(createPolicyEndpoint, "/", policy.ID)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
if err != nil {
return err
}
if _, err := c.do(req, &response); err != nil {
c.logger.Error("Failed to revoke access of the user from team", "Users", userId, req.URL)
return err
}
}

c.logger.Info("Remove access of the user from team,", "Users", userId, req.URL)
c.logger.Info("Remove access of the user from team", "Users", userId, req.URL)
return nil
}

func (c *client) RevokeProjectAccess(resource *Project, userId string, role string) error {
endPoint := path.Join(projectsEndpoint, "/", resource.ID, "/", role, "/", userId)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
endpoint := createPolicyEndpoint + "?projectId=" + resource.ID + "&userId=" + userId + "&roleId=" + role
req, err := c.newRequest(http.MethodGet, endpoint, "", "")
if err != nil {
return err
}

var success successAccess
var policies []*Policy
var response interface{}
if _, err := c.do(req, &response); err != nil {
return err
}

if v, ok := response.(map[string]interface{}); ok && v != nil {
err = mapstructure.Decode(v, &success)
err = mapstructure.Decode(v[policiesConst], &policies)
if err != nil {
return err
}
}

for _, policy := range policies {
endPoint := path.Join(createPolicyEndpoint, "/", policy.ID)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
if err != nil {
return err
}
if _, err := c.do(req, &response); err != nil {
c.logger.Error("Failed to revoke access of the user from project", "Users", userId, req.URL)
return err
}
}

c.logger.Info("Remove access of the user from project", "Users", userId, req.URL)
return nil
}

func (c *client) RevokeOrganizationAccess(resource *Organization, userId string, role string) error {
endPoint := path.Join(organizationEndpoint, "/", resource.ID, "/", role, "/", userId)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
endpoint := createPolicyEndpoint + "?orgId=" + resource.ID + "&userId=" + userId + "&roleId=" + role
req, err := c.newRequest(http.MethodGet, endpoint, "", "")
if err != nil {
return err
}

var success successAccess
var policies []*Policy
var response interface{}
if _, err := c.do(req, &response); err != nil {
return err
}

if v, ok := response.(map[string]interface{}); ok && v != nil {
err = mapstructure.Decode(v, &success)
err = mapstructure.Decode(v[policiesConst], &policies)
if err != nil {
return err
}
}

for _, policy := range policies {
endPoint := path.Join(createPolicyEndpoint, "/", policy.ID)
req, err := c.newRequest(http.MethodDelete, endPoint, "", "")
if err != nil {
return err
}
if _, err := c.do(req, &response); err != nil {
return err
}
}

c.logger.Info("Remove access of the user from organization", "Users", userId, req.URL)
Expand Down
Loading

0 comments on commit db13c4f

Please sign in to comment.