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

Add support for general permissions #132

Merged
merged 47 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e31e1b9
Added databricks_permissions resource
nfx Jun 19, 2020
e38a53f
Minor commenting tweaks
nfx Jun 22, 2020
732aeda
Register permissions api in the client
nfx Jun 22, 2020
cc6727a
Add pkg/errors
nfx Jun 22, 2020
78f8b72
Added real unit tests for resource
nfx Jun 23, 2020
8865b03
trigger build
nfx Jun 23, 2020
391e80f
trigger build
nfx Jun 23, 2020
4f5c730
Merge branch 'master' into permissions-api
stikkireddy Jun 24, 2020
20a38ae
bump aws-sdk-go from 1.32.2 to 1.32.6; bump terraform-plugin-sdk from…
stikkireddy Jun 25, 2020
6f50437
Add more readability for errors (#129)
nfx Jun 25, 2020
c7f2b3d
Added databricks_permissions resource
nfx Jun 19, 2020
686b435
Minor commenting tweaks
nfx Jun 22, 2020
4b15448
Register permissions api in the client
nfx Jun 22, 2020
1ed8448
Add pkg/errors
nfx Jun 22, 2020
57b2267
Added real unit tests for resource
nfx Jun 23, 2020
515d61e
trigger build
nfx Jun 23, 2020
f4a35e0
trigger build
nfx Jun 23, 2020
9b4cdf9
Fix code style violations
nfx Jun 25, 2020
ef4ed40
Added make install
nfx Jun 25, 2020
6656e48
make build will also test now (provider + client)
nfx Jun 25, 2020
7d09b9d
Added more coverage for edge cases on permissions
nfx Jun 25, 2020
b1bc6c3
Merge branch 'permissions-api' of github.com:databrickslabs/terraform…
nfx Jun 25, 2020
e8e85e1
try fixing build
nfx Jun 25, 2020
eeeb779
test linter failing build
nfx Jun 25, 2020
08a8b54
remove fmt from lint
nfx Jun 25, 2020
ecd11ec
format it back
nfx Jun 25, 2020
fe7d72e
Added databricks_permissions resource
nfx Jun 19, 2020
dd79a2d
Minor commenting tweaks
nfx Jun 22, 2020
0088df4
Register permissions api in the client
nfx Jun 22, 2020
259e467
Add pkg/errors
nfx Jun 22, 2020
e7cec50
Added real unit tests for resource
nfx Jun 23, 2020
9c9b589
trigger build
nfx Jun 23, 2020
88da24f
trigger build
nfx Jun 23, 2020
9fd9e38
Fix code style violations
nfx Jun 25, 2020
91d8243
Added make install
nfx Jun 25, 2020
47267fd
make build will also test now (provider + client)
nfx Jun 25, 2020
598f87a
Added more coverage for edge cases on permissions
nfx Jun 25, 2020
54d6893
Added databricks_permissions resource
nfx Jun 19, 2020
1dd443f
Minor commenting tweaks
nfx Jun 22, 2020
83dca55
trigger build
nfx Jun 23, 2020
6e69eb0
try fixing build
nfx Jun 25, 2020
458b2cf
test linter failing build
nfx Jun 25, 2020
70b1935
remove fmt from lint
nfx Jun 25, 2020
239f2a9
format it back
nfx Jun 25, 2020
5adeccc
Merge branch 'permissions-api' of github.com:databrickslabs/terraform…
nfx Jun 25, 2020
3fee89c
trigger build
nfx Jun 25, 2020
ed678fe
remove go get goimports
nfx Jun 25, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 60 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
We happily welcome contributions to databricks-terraform. We use GitHub Issues to track community reported issues and GitHub Pull Requests for accepting changes.
We happily welcome contributions to databricks-terraform. We use GitHub Issues to track community reported issues and GitHub Pull Requests for accepting changes.

## Unit testing resources

In order to unit test a resource, which runs fast and could be included in code coverage, one should use `ResourceTester`, that launches embedded HTTP server with `HTTPFixture`'s containing all calls that should have been made in given scenario:

```go
func TestPermissionsCreate(t *testing.T) {
_, err := ResourceTester(t, []HTTPFixture{
{
Method: http.MethodPatch,
// requires full URI
Resource: "/api/2.0/preview/permissions/clusters/abc",
// works with entities, not JSON. Diff is displayed in case of missmatch
ExpectedRequest: model.AccessControlChangeList{
AccessControlList: []*model.AccessControlChange{
{
UserName: &TestingUser,
PermissionLevel: "CAN_USE",
},
},
},
},
{
Method: http.MethodGet,
Resource: "/api/2.0/preview/permissions/clusters/abc?",
Response: model.AccessControlChangeList{
AccessControlList: []*model.AccessControlChange{
{
UserName: &TestingUser,
PermissionLevel: "CAN_MANAGE",
},
},
},
},
{
Method: http.MethodGet,
Resource: "/api/2.0/preview/scim/v2/Me?",
Response: model.User{
UserName: "chuck.norris",
},
},
},
// next argument is function, that creates resource (to make schema for ResourceData)
resourcePermissions,
// state represented as native structure (though a bit clunky)
map[string]interface{}{
"cluster_id": "abc",
"access_control": []interface{}{
map[string]interface{}{
"user_name": TestingUser,
"permission_level": "CAN_USE",
},
},
},
// the last argument is a function, that performs a stage on resource (Create/update/delete/read)
resourcePermissionsCreate)
assert.NoError(t, err, err)
}
```
75 changes: 75 additions & 0 deletions client/model/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package model

// ObjectACL is a structure to generically describe access control
type ObjectACL struct {
ObjectID string `json:"object_id,omitempty"`
ObjectType string `json:"object_type,omitempty"`
AccessControlList []*AccessControl `json:"access_control_list"`
}

// AccessControl is a structure to describe user/group permissions
type AccessControl struct {
UserName *string `json:"user_name,omitempty"`
GroupName *string `json:"group_name,omitempty"`
AllPermissions []*Permission `json:"all_permissions,omitempty"`
}

// Permission is a structure to describe permission level
type Permission struct {
PermissionLevel string `json:"permission_level"`
Inherited bool `json:"inherited,omitempty"`
InheritedFromObject []string `json:"inherited_from_object,omitempty"`
}

// AccessControlChangeList is wrapper around ACL changes for REST API
type AccessControlChangeList struct {
AccessControlList []*AccessControlChange `json:"access_control_list"`
}

// AccessControlChange is API wrapper for changing permissions
type AccessControlChange struct {
UserName *string `json:"user_name,omitempty"`
GroupName *string `json:"group_name,omitempty"`
ServicePrincipalName *string `json:"service_principal_name,omitempty"`
PermissionLevel string `json:"permission_level"`
}

// ToAccessControlChangeList converts data formats
func (oa *ObjectACL) ToAccessControlChangeList() *AccessControlChangeList {
acl := new(AccessControlChangeList)
for _, accessControl := range oa.AccessControlList {
for _, permission := range accessControl.AllPermissions {
if permission.Inherited {
continue
}
item := new(AccessControlChange)
acl.AccessControlList = append(acl.AccessControlList, item)
item.PermissionLevel = permission.PermissionLevel
if accessControl.UserName != nil {
item.UserName = accessControl.UserName
} else if accessControl.GroupName != nil {
item.GroupName = accessControl.GroupName
}
}
}
return acl
}

// AccessControl exports data for TF
func (acl *AccessControlChangeList) AccessControl(me string) []map[string]string {
result := []map[string]string{}
for _, control := range acl.AccessControlList {
item := map[string]string{}
if control.UserName != nil && *control.UserName != "" {
if me == *control.UserName {
continue
}
item["user_name"] = *control.UserName
} else if control.GroupName != nil && *control.GroupName != "" {
item["group_name"] = *control.GroupName
}
item["permission_level"] = control.PermissionLevel
result = append(result, item)
}
return result
}
5 changes: 5 additions & 0 deletions client/service/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ func (c *DBApiClient) MWSCustomerManagedKeys() MWSCustomerManagedKeysAPI {
return MWSCustomerManagedKeysAPI{Client: c}
}

// Permissions returns an instance of CommandsAPI
func (c *DBApiClient) Permissions() PermissionsAPI {
return PermissionsAPI{Client: c}
}

func (c *DBApiClient) performQuery(method, path string, apiVersion string, headers map[string]string, data interface{}, secretsMask *SecretsMask) ([]byte, error) {
err := c.Config.getOrCreateToken()
if err != nil {
Expand Down
50 changes: 50 additions & 0 deletions client/service/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package service

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

"github.com/databrickslabs/databricks-terraform/client/model"
)

// PermissionsAPI exposes general permission related methods
type PermissionsAPI struct {
Client *DBApiClient
}

// AddOrModify works with permissions change list
func (a PermissionsAPI) AddOrModify(objectID string, objectACL *model.AccessControlChangeList) error {
_, err := a.Client.performQuery(http.MethodPatch,
"/preview/permissions"+objectID,
"2.0", nil, objectACL, nil)
if err != nil {
return err
}

return err
}

// SetOrDelete updates object permissions
func (a PermissionsAPI) SetOrDelete(objectID string, objectACL *model.AccessControlChangeList) error {
_, err := a.Client.performQuery(http.MethodPut,
"/preview/permissions"+objectID,
"2.0", nil, objectACL, nil)
if err != nil {
return err
}

return err
}

// Read gets all relevant permissions for the object, including inherited ones
func (a PermissionsAPI) Read(objectID string) (*model.ObjectACL, error) {
resp, err := a.Client.performQuery(http.MethodGet,
"/preview/permissions"+objectID,
"2.0", nil, nil, nil)
if err != nil {
return nil, err
}
var objectACL = new(model.ObjectACL)
err = json.Unmarshal(resp, &objectACL)
return objectACL, err
}
11 changes: 9 additions & 2 deletions client/service/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,21 @@ func (a UsersAPI) Read(userID string) (model.User, error) {
}

func (a UsersAPI) read(userID string) (model.User, error) {
var user model.User
userPath := fmt.Sprintf("/preview/scim/v2/Users/%v", userID)
return a.readByPath(userPath)
}

// Me gets user information about caller
func (a UsersAPI) Me() (model.User, error) {
return a.readByPath("/preview/scim/v2/Me")
}

func (a UsersAPI) readByPath(userPath string) (model.User, error) {
var user model.User
resp, err := a.Client.performQuery(http.MethodGet, userPath, "2.0", scimHeaders, nil, nil)
if err != nil {
return user, err
}

err = json.Unmarshal(resp, &user)
return user, err
}
Expand Down
1 change: 1 addition & 0 deletions databricks/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Provider(version string) terraform.ResourceProvider {
"databricks_secret_scope": resourceSecretScope(),
"databricks_secret": resourceSecret(),
"databricks_secret_acl": resourceSecretACL(),
"databricks_permissions": resourcePermissions(),
"databricks_instance_pool": resourceInstancePool(),
"databricks_scim_user": resourceScimUser(),
"databricks_scim_group": resourceScimGroup(),
Expand Down