odps/security/manager.go (306 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 security
import (
"bytes"
"encoding/json"
"encoding/xml"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/pkg/errors"
"github.com/aliyun/aliyun-odps-go-sdk/odps/common"
"github.com/aliyun/aliyun-odps-go-sdk/odps/restclient"
)
type Manager struct {
restClient restclient.RestClient
projectName string
securityConfig Config
}
func NewSecurityManager(restClient restclient.RestClient, projectName string) Manager {
return Manager{
restClient: restClient,
projectName: projectName,
securityConfig: NewSecurityConfig(restClient, false, projectName),
}
}
func (sm *Manager) GetSecurityConfig(withoutExceptionPolicy bool) (Config, error) {
if sm.securityConfig.BeLoaded() {
return sm.securityConfig, nil
}
sm.securityConfig.withoutExceptionPolicy = withoutExceptionPolicy
err := sm.securityConfig.Load()
return sm.securityConfig, errors.WithStack(err)
}
func (sm *Manager) SetSecurityConfig(config Config, supervisionToken string) error {
err := config.Update(supervisionToken)
sm.securityConfig = config
return errors.WithStack(err)
}
func (sm *Manager) CheckPermissionV1(p Permission) (*PermissionCheckResult, error) {
rb := sm.rb()
resource := rb.Auth()
client := sm.restClient
type ResModel struct {
XMLName xml.Name `xml:"Auth"`
PermissionCheckResult
}
body, err := json.Marshal(p)
if err != nil {
return nil, errors.WithStack(err)
}
req, err := client.NewRequest(common.HttpMethod.PostMethod, resource, bytes.NewReader(body))
req.Header.Set(common.HttpHeaderContentType, "application/json")
var resModel ResModel
err = client.DoWithModel(req, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
return &resModel.PermissionCheckResult, nil
}
func (sm *Manager) CheckPermissionV0(
objectType PermissionObjectType,
objectName string,
actionType PermissionActionType,
columns []string,
) (*PermissionCheckResult, error) {
rb := sm.rb()
resource := rb.Auth()
client := sm.restClient
queryArgs := make(url.Values, 4)
queryArgs.Set("type", objectType.String())
queryArgs.Set("name", objectName)
queryArgs.Set("grantee", actionType.String())
if len(columns) > 0 {
queryArgs.Set("columns", strings.Join(columns, ","))
}
type ResModel struct {
XMLName xml.Name `xml:"Auth"`
PermissionCheckResult
}
var resModel ResModel
err := client.GetWithModel(resource, queryArgs, nil, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
return &resModel.PermissionCheckResult, nil
}
func (sm *Manager) getPolicy(resource, policyType string) ([]byte, error) {
queryArgs := make(url.Values, 1)
queryArgs.Set(policyType, "")
client := sm.restClient
var body []byte
err := client.GetWithParseFunc(resource, queryArgs, nil, func(res *http.Response) error {
var err error
// Use ioutil.ReadAll instead of io.ReadAll for compatibility with Go 1.15.
body, err = ioutil.ReadAll(res.Body)
return errors.WithStack(err)
})
return body, errors.WithStack(err)
}
func (sm *Manager) setPolicy(resource, policyType string, policy string) error {
queryArgs := make(url.Values, 1)
queryArgs.Set(policyType, "")
client := sm.restClient
req, err := client.NewRequestWithUrlQuery(common.HttpMethod.PutMethod, resource, strings.NewReader(policy), queryArgs)
if err != nil {
return errors.WithStack(err)
}
if policyType == "security_policy" {
req.Header.Set(common.HttpHeaderContentType, "application/json")
}
return errors.WithStack(client.DoWithParseFunc(req, nil))
}
func (sm *Manager) GetPolicy() ([]byte, error) {
rb := sm.rb()
return sm.getPolicy(rb.Project(), "policy")
}
func (sm *Manager) SetPolicy(policy string) error {
rb := sm.rb()
return sm.setPolicy(rb.Project(), "policy", policy)
}
func (sm *Manager) GetSecurityPolicy() ([]byte, error) {
rb := sm.rb()
return sm.getPolicy(rb.Project(), "security_policy")
}
func (sm *Manager) SetSecurityPolicy(policy string) error {
rb := sm.rb()
return sm.setPolicy(rb.Project(), "security_policy", policy)
}
func (sm *Manager) GetRolePolicy(roleName string) ([]byte, error) {
rb := sm.rb()
return sm.getPolicy(rb.Role(roleName), "policy")
}
func (sm *Manager) SetRolePolicy(roleName, policy string) error {
rb := sm.rb()
return sm.setPolicy(rb.Role(roleName), "policy", policy)
}
func (sm *Manager) ListUsers() ([]User, error) {
rb := sm.rb()
resource := rb.Users()
client := sm.restClient
type ResModel struct {
XMLName xml.Name `xml:"Users"`
User []userModel `xml:"User"`
}
var resModel ResModel
err := client.GetWithModel(resource, nil, nil, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
users := make([]User, len(resModel.User))
for i, model := range resModel.User {
user := NewUser(model.ID, sm.restClient, sm.projectName)
user.model = model
users[i] = user
}
return users, nil
}
func (sm *Manager) ListRoles() ([]Role, error) {
rb := sm.rb()
resource := rb.Roles()
client := sm.restClient
type ResModel struct {
XMLName xml.Name `xml:"Roles"`
Role []roleModel `xml:"Role"`
}
var resModel ResModel
err := client.GetWithModel(resource, nil, nil, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
roles := make([]Role, len(resModel.Role))
for i, model := range resModel.Role {
role := NewRole(model.Name, sm.restClient, sm.projectName)
role.model = model
roles[i] = role
}
return roles, nil
}
func (sm *Manager) listRolesForUser(userIdOrName, _type string) ([]Role, error) {
rb := sm.rb()
resource := rb.User(userIdOrName)
client := sm.restClient
queryArgs := make(url.Values, 2)
queryArgs.Set("role", "")
if _type != "" {
queryArgs.Set("type", _type)
}
type ResModel struct {
XMLName xml.Name `xml:"Roles"`
Role []roleModel `xml:"Role"`
}
var resModel ResModel
err := client.GetWithModel(resource, nil, nil, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
roles := make([]Role, len(resModel.Role))
for i, model := range resModel.Role {
role := NewRole(model.Name, sm.restClient, sm.projectName)
role.model = model
roles[i] = role
}
return roles, nil
}
func (sm *Manager) ListRolesForUserWithName(userName string) ([]Role, error) {
return sm.listRolesForUser(userName, "displayname")
}
func (sm *Manager) ListRolesForUserWithId(userId, _type string) ([]Role, error) {
return sm.listRolesForUser(userId, "")
}
func (sm *Manager) ListUsersForRole(roleName string) ([]User, error) {
rb := sm.rb()
resource := rb.Role(roleName)
client := sm.restClient
queryArgs := make(url.Values, 1)
queryArgs.Set("usesrs", "")
type ResModel struct {
XMLName xml.Name `xml:"Users"`
User []userModel `xml:"User"`
}
var resModel ResModel
err := client.GetWithModel(resource, queryArgs, nil, &resModel)
if err != nil {
return nil, errors.WithStack(err)
}
users := make([]User, len(resModel.User))
for i, model := range resModel.User {
user := NewUser(model.ID, sm.restClient, sm.projectName)
user.model = model
users[i] = user
}
return users, nil
}
func (sm *Manager) RunQuery(query string, jsonOutput bool, supervisionToken string) (string, error) {
authIns, err := sm.Run(query, jsonOutput, supervisionToken)
if err != nil {
return "", err
}
return authIns.WaitForSuccess()
}
func (sm *Manager) Run(query string, jsonOutput bool, supervisionToken string) (*AuthQueryInstance, error) {
rb := sm.rb()
resource := rb.Authorization()
client := sm.restClient
type ReqBody struct {
XMLName xml.Name `xml:"Authorization"`
Query string
ResponseInJsonFormat bool
}
type ResModel struct {
XMLName xml.Name `xml:"Authorization"`
Result string
}
reqBody := ReqBody{
Query: query,
ResponseInJsonFormat: jsonOutput,
}
var resModel ResModel
var isAsync bool
headers := make(map[string]string)
if supervisionToken != "" {
headers["odps-x-supervision-token"] = supervisionToken
}
err := client.DoXmlWithParseRes(common.HttpMethod.PostMethod, resource, nil, nil, reqBody, func(res *http.Response) error {
if res.StatusCode < 200 || res.StatusCode >= 300 {
return errors.WithStack(restclient.NewHttpNotOk(res))
}
isAsync = res.StatusCode != 200
decoder := xml.NewDecoder(res.Body)
return errors.WithStack(decoder.Decode(&resModel))
})
if _, ok := err.(restclient.HttpError); ok {
return nil, errors.WithStack(err)
}
if err != nil {
return nil, errors.WithStack(err)
}
if !isAsync {
return newAuthQueryInstanceWithResult(resModel.Result), nil
}
return newAuthQueryInstance(sm, resModel.Result), nil
}
func (sm *Manager) GenerateAuthorizationToken(policy string) (string, error) {
rb := sm.rb()
resource := rb.Authorization()
client := sm.restClient
queryArgs := make(url.Values, 1)
queryArgs.Set("sign_bearer_token", "")
req, err := client.NewRequestWithUrlQuery(common.HttpMethod.PostMethod, resource, strings.NewReader(policy), queryArgs)
if err != nil {
return "", errors.WithStack(err)
}
req.Header.Set(common.HttpHeaderContentType, "application/json")
type ResModel struct {
XMLName xml.Name `xml:"Authorization"`
Result string
}
var resModel ResModel
err = client.DoWithModel(req, &resModel)
if err != nil {
return "", errors.WithStack(err)
}
return resModel.Result, nil
}
func (sm *Manager) rb() common.ResourceBuilder {
return common.NewResourceBuilder(sm.projectName)
}