mirror of
https://github.com/minio/minio.git
synced 2025-04-23 20:05:55 -04:00
Add canned policy support (#6637)
This PR adds an additional API where we can create a new set of canned policies which can be used with one or many users.
This commit is contained in:
parent
c7f180ffa9
commit
1e7e5e297c
@ -106,7 +106,7 @@ func (a adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Re
|
|||||||
// of read-quorum availability.
|
// of read-quorum availability.
|
||||||
uptime, err := getPeerUptimes(globalAdminPeers)
|
uptime, err := getPeerUptimes(globalAdminPeers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponseJSON(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
logger.LogIf(context.Background(), err)
|
logger.LogIf(context.Background(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -731,41 +731,7 @@ func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
accessKey := vars["accessKey"]
|
accessKey := vars["accessKey"]
|
||||||
if err := globalIAMSys.DeleteUser(accessKey); err != nil {
|
if err := globalIAMSys.DeleteUser(accessKey); err != nil {
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
writeErrorResponseJSON(w, ErrInternalError, r.URL)
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveUserPolicy - DELETE /minio/admin/v1/remove-user-policy?accessKey=<access_key>
|
|
||||||
func (a adminAPIHandlers) RemoveUserPolicy(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "RemoveUserPolicy")
|
|
||||||
|
|
||||||
// Get current object layer instance.
|
|
||||||
objectAPI := newObjectLayerFn()
|
|
||||||
if objectAPI == nil {
|
|
||||||
writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate request signature.
|
|
||||||
adminAPIErr := checkAdminRequestAuthType(r, "")
|
|
||||||
if adminAPIErr != ErrNone {
|
|
||||||
writeErrorResponseJSON(w, adminAPIErr, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
accessKey := vars["accessKey"]
|
|
||||||
if err := globalIAMSys.DeletePolicy(accessKey); err != nil {
|
|
||||||
logger.LogIf(ctx, err)
|
|
||||||
writeErrorResponseJSON(w, ErrInternalError, r.URL)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,14 +833,140 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil {
|
if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil {
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
writeErrorResponseJSON(w, ErrInternalError, r.URL)
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddUserPolicy - PUT /minio/admin/v1/add-user-policy?accessKey=<access_key>
|
// ListCannedPolicies - GET /minio/admin/v1/list-canned-policies
|
||||||
func (a adminAPIHandlers) AddUserPolicy(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "AddUserPolicy")
|
ctx := newContext(r, w, "ListCannedPolicies")
|
||||||
|
|
||||||
|
// Get current object layer instance.
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate request signature.
|
||||||
|
adminAPIErr := checkAdminRequestAuthType(r, "")
|
||||||
|
if adminAPIErr != ErrNone {
|
||||||
|
writeErrorResponseJSON(w, adminAPIErr, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policies, err := globalIAMSys.ListCannedPolicies()
|
||||||
|
if err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewEncoder(w).Encode(policies); err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCannedPolicy - DELETE /minio/admin/v1/remove-canned-policy?name=<policy_name>
|
||||||
|
func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "RemoveCannedPolicy")
|
||||||
|
|
||||||
|
// Get current object layer instance.
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyName := vars["name"]
|
||||||
|
|
||||||
|
// Validate request signature.
|
||||||
|
adminAPIErr := checkAdminRequestAuthType(r, "")
|
||||||
|
if adminAPIErr != ErrNone {
|
||||||
|
writeErrorResponseJSON(w, adminAPIErr, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := globalIAMSys.DeleteCannedPolicy(policyName); err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCannedPolicy - PUT /minio/admin/v1/add-canned-policy?name=<policy_name>
|
||||||
|
func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "AddCannedPolicy")
|
||||||
|
|
||||||
|
// Get current object layer instance.
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyName := vars["name"]
|
||||||
|
|
||||||
|
// Validate request signature.
|
||||||
|
adminAPIErr := checkAdminRequestAuthType(r, "")
|
||||||
|
if adminAPIErr != ErrNone {
|
||||||
|
writeErrorResponseJSON(w, adminAPIErr, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out if Content-Length is missing.
|
||||||
|
if r.ContentLength <= 0 {
|
||||||
|
writeErrorResponseJSON(w, ErrMissingContentLength, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out if Content-Length is beyond allowed size.
|
||||||
|
if r.ContentLength > maxBucketPolicySize {
|
||||||
|
writeErrorResponseJSON(w, ErrEntityTooLarge, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength))
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(w, ErrMalformedPolicy, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version in policy must not be empty
|
||||||
|
if iamPolicy.Version == "" {
|
||||||
|
writeErrorResponseJSON(w, ErrMalformedPolicy, r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = globalIAMSys.SetCannedPolicy(policyName, *iamPolicy); err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserPolicy - PUT /minio/admin/v1/set-user-policy?accessKey=<access_key>&name=<policy_name>
|
||||||
|
func (a adminAPIHandlers) SetUserPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "SetUserPolicy")
|
||||||
|
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := newObjectLayerFn()
|
objectAPI := newObjectLayerFn()
|
||||||
@ -885,6 +977,7 @@ func (a adminAPIHandlers) AddUserPolicy(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
accessKey := vars["accessKey"]
|
accessKey := vars["accessKey"]
|
||||||
|
policyName := vars["name"]
|
||||||
|
|
||||||
// Validate request signature.
|
// Validate request signature.
|
||||||
adminAPIErr := checkAdminRequestAuthType(r, "")
|
adminAPIErr := checkAdminRequestAuthType(r, "")
|
||||||
@ -901,38 +994,13 @@ func (a adminAPIHandlers) AddUserPolicy(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
// Custom IAM policies not allowed for admin user.
|
// Custom IAM policies not allowed for admin user.
|
||||||
if accessKey == globalServerConfig.GetCredential().AccessKey {
|
if accessKey == globalServerConfig.GetCredential().AccessKey {
|
||||||
writeErrorResponse(w, ErrInvalidRequest, r.URL)
|
writeErrorResponseJSON(w, ErrInvalidRequest, r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error out if Content-Length is missing.
|
if err := globalIAMSys.SetUserPolicy(accessKey, policyName); err != nil {
|
||||||
if r.ContentLength <= 0 {
|
|
||||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error out if Content-Length is beyond allowed size.
|
|
||||||
if r.ContentLength > maxBucketPolicySize {
|
|
||||||
writeErrorResponse(w, ErrEntityTooLarge, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength))
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponse(w, ErrMalformedPolicy, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version in policy must not be empty
|
|
||||||
if iamPolicy.Version == "" {
|
|
||||||
writeErrorResponse(w, ErrMalformedPolicy, r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = globalIAMSys.SetPolicy(accessKey, *iamPolicy); err != nil {
|
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1198,7 +1266,7 @@ func (a adminAPIHandlers) UpdateAdminCredentialsHandler(w http.ResponseWriter,
|
|||||||
|
|
||||||
creds, err := auth.CreateCredentials(req.AccessKey, req.SecretKey)
|
creds, err := auth.CreateCredentials(req.AccessKey, req.SecretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponseJSON(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,15 +81,23 @@ func registerAdminRouter(router *mux.Router) {
|
|||||||
|
|
||||||
// -- IAM APIs --
|
// -- IAM APIs --
|
||||||
|
|
||||||
|
// Add policy IAM
|
||||||
|
adminV1Router.Methods(http.MethodPut).Path("/add-canned-policy").HandlerFunc(httpTraceHdrs(adminAPI.AddCannedPolicy)).Queries("name", "{name:.*}")
|
||||||
|
|
||||||
// Add user IAM
|
// Add user IAM
|
||||||
adminV1Router.Methods(http.MethodPut).Path("/add-user").HandlerFunc(httpTraceHdrs(adminAPI.AddUser)).Queries("accessKey", "{accessKey:.*}")
|
adminV1Router.Methods(http.MethodPut).Path("/add-user").HandlerFunc(httpTraceHdrs(adminAPI.AddUser)).Queries("accessKey", "{accessKey:.*}")
|
||||||
adminV1Router.Methods(http.MethodPut).Path("/add-user-policy").HandlerFunc(httpTraceHdrs(adminAPI.AddUserPolicy)).Queries("accessKey", "{accessKey:.*}")
|
adminV1Router.Methods(http.MethodPut).Path("/set-user-policy").HandlerFunc(httpTraceHdrs(adminAPI.SetUserPolicy)).
|
||||||
|
Queries("accessKey", "{accessKey:.*}").Queries("name", "{name:.*}")
|
||||||
|
|
||||||
|
// Remove policy IAM
|
||||||
|
adminV1Router.Methods(http.MethodDelete).Path("/remove-canned-policy").HandlerFunc(httpTraceHdrs(adminAPI.RemoveCannedPolicy)).Queries("name", "{name:.*}")
|
||||||
|
|
||||||
// Remove user IAM
|
// Remove user IAM
|
||||||
adminV1Router.Methods(http.MethodDelete).Path("/remove-user").HandlerFunc(httpTraceHdrs(adminAPI.RemoveUser)).Queries("accessKey", "{accessKey:.*}")
|
adminV1Router.Methods(http.MethodDelete).Path("/remove-user").HandlerFunc(httpTraceHdrs(adminAPI.RemoveUser)).Queries("accessKey", "{accessKey:.*}")
|
||||||
adminV1Router.Methods(http.MethodDelete).Path("/remove-user-policy").HandlerFunc(httpTraceHdrs(adminAPI.RemoveUserPolicy)).Queries("accessKey", "{accessKey:.*}")
|
|
||||||
|
|
||||||
// List users
|
// List users
|
||||||
adminV1Router.Methods(http.MethodGet).Path("/list-users").HandlerFunc(httpTraceHdrs(adminAPI.ListUsers))
|
adminV1Router.Methods(http.MethodGet).Path("/list-users").HandlerFunc(httpTraceHdrs(adminAPI.ListUsers))
|
||||||
|
|
||||||
|
// List policies
|
||||||
|
adminV1Router.Methods(http.MethodGet).Path("/list-canned-policies").HandlerFunc(httpTraceHdrs(adminAPI.ListCannedPolicies))
|
||||||
}
|
}
|
||||||
|
@ -187,6 +187,9 @@ const (
|
|||||||
// new error codes here.
|
// new error codes here.
|
||||||
|
|
||||||
ErrMalformedJSON
|
ErrMalformedJSON
|
||||||
|
ErrAdminNoSuchUser
|
||||||
|
ErrAdminNoSuchPolicy
|
||||||
|
ErrAdminInvalidArgument
|
||||||
ErrAdminInvalidAccessKey
|
ErrAdminInvalidAccessKey
|
||||||
ErrAdminInvalidSecretKey
|
ErrAdminInvalidSecretKey
|
||||||
ErrAdminConfigNoQuorum
|
ErrAdminConfigNoQuorum
|
||||||
@ -864,6 +867,21 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
|||||||
Description: "The JSON you provided was not well-formed or did not validate against our published format.",
|
Description: "The JSON you provided was not well-formed or did not validate against our published format.",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
ErrAdminNoSuchUser: {
|
||||||
|
Code: "XMinioAdminNoSuchUser",
|
||||||
|
Description: "The specified user does not exist.",
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
ErrAdminNoSuchPolicy: {
|
||||||
|
Code: "XMinioAdminNoSuchPolicy",
|
||||||
|
Description: "The canned policy does not exist.",
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
ErrAdminInvalidArgument: {
|
||||||
|
Code: "XMinioAdminInvalidArgument",
|
||||||
|
Description: "Invalid arguments specified.",
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
},
|
||||||
ErrAdminInvalidAccessKey: {
|
ErrAdminInvalidAccessKey: {
|
||||||
Code: "XMinioAdminInvalidAccessKey",
|
Code: "XMinioAdminInvalidAccessKey",
|
||||||
Description: "The access key is invalid.",
|
Description: "The access key is invalid.",
|
||||||
@ -1428,6 +1446,12 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) {
|
|||||||
|
|
||||||
// Verify if the underlying error is signature mismatch.
|
// Verify if the underlying error is signature mismatch.
|
||||||
switch err {
|
switch err {
|
||||||
|
case errInvalidArgument:
|
||||||
|
apiErr = ErrAdminInvalidArgument
|
||||||
|
case errNoSuchUser:
|
||||||
|
apiErr = ErrAdminNoSuchUser
|
||||||
|
case errNoSuchPolicy:
|
||||||
|
apiErr = ErrAdminNoSuchPolicy
|
||||||
case errSignatureMismatch:
|
case errSignatureMismatch:
|
||||||
apiErr = ErrSignatureDoesNotMatch
|
apiErr = ErrSignatureDoesNotMatch
|
||||||
case errInvalidRange:
|
case errInvalidRange:
|
||||||
|
339
cmd/iam.go
339
cmd/iam.go
@ -39,6 +39,9 @@ const (
|
|||||||
// IAM users directory.
|
// IAM users directory.
|
||||||
iamConfigUsersPrefix = iamConfigPrefix + "/users/"
|
iamConfigUsersPrefix = iamConfigPrefix + "/users/"
|
||||||
|
|
||||||
|
// IAM policies directory.
|
||||||
|
iamConfigPoliciesPrefix = iamConfigPrefix + "/policies/"
|
||||||
|
|
||||||
// IAM sts directory.
|
// IAM sts directory.
|
||||||
iamConfigSTSPrefix = iamConfigPrefix + "/sts/"
|
iamConfigSTSPrefix = iamConfigPrefix + "/sts/"
|
||||||
|
|
||||||
@ -52,8 +55,9 @@ const (
|
|||||||
// IAMSys - config system.
|
// IAMSys - config system.
|
||||||
type IAMSys struct {
|
type IAMSys struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
iamUsersMap map[string]auth.Credentials
|
iamUsersMap map[string]auth.Credentials
|
||||||
iamPolicyMap map[string]iampolicy.Policy
|
iamPolicyMap map[string]string
|
||||||
|
iamCannedPolicyMap map[string]iampolicy.Policy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load - load iam.json
|
// Load - load iam.json
|
||||||
@ -109,87 +113,19 @@ func (sys *IAMSys) Init(objAPI ObjectLayer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPolicy - sets policy to given user name. If policy is empty,
|
// DeleteCannedPolicy - deletes a canned policy.
|
||||||
// existing policy is removed.
|
func (sys *IAMSys) DeleteCannedPolicy(policyName string) error {
|
||||||
func (sys *IAMSys) SetPolicy(accessKey string, p iampolicy.Policy) error {
|
|
||||||
objectAPI := newObjectLayerFn()
|
objectAPI := newObjectLayerFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
configFile := pathJoin(iamConfigUsersPrefix, accessKey, iamPolicyFile)
|
if policyName == "" {
|
||||||
data, err := json.Marshal(p)
|
return errInvalidArgument
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
|
||||||
if err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = saveConfig(context.Background(), objectAPI, configFile, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sys.Lock()
|
|
||||||
defer sys.Unlock()
|
|
||||||
|
|
||||||
if p.IsEmpty() {
|
|
||||||
delete(sys.iamPolicyMap, accessKey)
|
|
||||||
} else {
|
|
||||||
sys.iamPolicyMap[accessKey] = p
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveTempPolicy - this is used for temporary credentials only.
|
|
||||||
func (sys *IAMSys) SaveTempPolicy(accessKey string, p iampolicy.Policy) error {
|
|
||||||
objectAPI := newObjectLayerFn()
|
|
||||||
if objectAPI == nil {
|
|
||||||
return errServerNotInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
configFile := pathJoin(iamConfigSTSPrefix, accessKey, iamPolicyFile)
|
|
||||||
data, err := json.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
|
||||||
if err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = saveConfig(context.Background(), objectAPI, configFile, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sys.Lock()
|
|
||||||
defer sys.Unlock()
|
|
||||||
|
|
||||||
if p.IsEmpty() {
|
|
||||||
delete(sys.iamPolicyMap, accessKey)
|
|
||||||
} else {
|
|
||||||
sys.iamPolicyMap[accessKey] = p
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePolicy - sets policy to given user name. If policy is empty,
|
|
||||||
// existing policy is removed.
|
|
||||||
func (sys *IAMSys) DeletePolicy(accessKey string) error {
|
|
||||||
objectAPI := newObjectLayerFn()
|
|
||||||
if objectAPI == nil {
|
|
||||||
return errServerNotInitialized
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
configFile := pathJoin(iamConfigUsersPrefix, accessKey, iamPolicyFile)
|
configFile := pathJoin(iamConfigPoliciesPrefix, policyName, iamPolicyFile)
|
||||||
if globalEtcdClient != nil {
|
if globalEtcdClient != nil {
|
||||||
err = deleteConfigEtcd(context.Background(), globalEtcdClient, configFile)
|
err = deleteConfigEtcd(context.Background(), globalEtcdClient, configFile)
|
||||||
} else {
|
} else {
|
||||||
@ -199,11 +135,104 @@ func (sys *IAMSys) DeletePolicy(accessKey string) error {
|
|||||||
sys.Lock()
|
sys.Lock()
|
||||||
defer sys.Unlock()
|
defer sys.Unlock()
|
||||||
|
|
||||||
delete(sys.iamPolicyMap, accessKey)
|
delete(sys.iamCannedPolicyMap, policyName)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListCannedPolicies - lists all canned policies.
|
||||||
|
func (sys *IAMSys) ListCannedPolicies() (map[string][]byte, error) {
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
return nil, errServerNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
var cannedPolicyMap = make(map[string][]byte)
|
||||||
|
|
||||||
|
sys.RLock()
|
||||||
|
defer sys.RUnlock()
|
||||||
|
|
||||||
|
for k, v := range sys.iamCannedPolicyMap {
|
||||||
|
data, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cannedPolicyMap[k] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
return cannedPolicyMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCannedPolicy - sets a new canned policy.
|
||||||
|
func (sys *IAMSys) SetCannedPolicy(policyName string, p iampolicy.Policy) error {
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
return errServerNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.IsEmpty() || policyName == "" {
|
||||||
|
return errInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := pathJoin(iamConfigPoliciesPrefix, policyName, iamPolicyFile)
|
||||||
|
data, err := json.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if globalEtcdClient != nil {
|
||||||
|
err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data)
|
||||||
|
} else {
|
||||||
|
err = saveConfig(context.Background(), objectAPI, configFile, data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.Lock()
|
||||||
|
defer sys.Unlock()
|
||||||
|
|
||||||
|
sys.iamCannedPolicyMap[policyName] = p
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserPolicy - sets policy to given user name.
|
||||||
|
func (sys *IAMSys) SetUserPolicy(accessKey, policyName string) error {
|
||||||
|
objectAPI := newObjectLayerFn()
|
||||||
|
if objectAPI == nil {
|
||||||
|
return errServerNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.Lock()
|
||||||
|
defer sys.Unlock()
|
||||||
|
|
||||||
|
if _, ok := sys.iamUsersMap[accessKey]; !ok {
|
||||||
|
return errNoSuchUser
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := sys.iamCannedPolicyMap[policyName]; !ok {
|
||||||
|
return errNoSuchPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(policyName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := pathJoin(iamConfigUsersPrefix, accessKey, iamPolicyFile)
|
||||||
|
if globalEtcdClient != nil {
|
||||||
|
err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data)
|
||||||
|
} else {
|
||||||
|
err = saveConfig(context.Background(), objectAPI, configFile, data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.iamPolicyMap[accessKey] = policyName
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteUser - set user credentials.
|
// DeleteUser - set user credentials.
|
||||||
func (sys *IAMSys) DeleteUser(accessKey string) error {
|
func (sys *IAMSys) DeleteUser(accessKey string) error {
|
||||||
objectAPI := newObjectLayerFn()
|
objectAPI := newObjectLayerFn()
|
||||||
@ -212,17 +241,30 @@ func (sys *IAMSys) DeleteUser(accessKey string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
configFile := pathJoin(iamConfigUsersPrefix, accessKey, iamPolicyFile)
|
pFile := pathJoin(iamConfigUsersPrefix, accessKey, iamPolicyFile)
|
||||||
|
iFile := pathJoin(iamConfigUsersPrefix, accessKey, iamIdentityFile)
|
||||||
if globalEtcdClient != nil {
|
if globalEtcdClient != nil {
|
||||||
err = deleteConfigEtcd(context.Background(), globalEtcdClient, configFile)
|
// It is okay to ingnore errors when deleting policy.json for the user.
|
||||||
|
_ = deleteConfigEtcd(context.Background(), globalEtcdClient, pFile)
|
||||||
|
err = deleteConfigEtcd(context.Background(), globalEtcdClient, iFile)
|
||||||
} else {
|
} else {
|
||||||
err = deleteConfig(context.Background(), objectAPI, configFile)
|
// It is okay to ingnore errors when deleting policy.json for the user.
|
||||||
|
_ = deleteConfig(context.Background(), objectAPI, pFile)
|
||||||
|
err = deleteConfig(context.Background(), objectAPI, iFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
switch err.(type) {
|
||||||
|
case ObjectNotFound:
|
||||||
|
err = errNoSuchUser
|
||||||
}
|
}
|
||||||
|
|
||||||
sys.Lock()
|
sys.Lock()
|
||||||
defer sys.Unlock()
|
defer sys.Unlock()
|
||||||
|
|
||||||
delete(sys.iamUsersMap, accessKey)
|
delete(sys.iamUsersMap, accessKey)
|
||||||
|
delete(sys.iamPolicyMap, accessKey)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,13 +282,13 @@ func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
if globalEtcdClient != nil {
|
||||||
if err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data); err != nil {
|
err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data)
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if err = saveConfig(context.Background(), objectAPI, configFile, data); err != nil {
|
err = saveConfig(context.Background(), objectAPI, configFile, data)
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sys.Lock()
|
sys.Lock()
|
||||||
@ -270,7 +312,8 @@ func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
|
|||||||
|
|
||||||
for k, v := range sys.iamUsersMap {
|
for k, v := range sys.iamUsersMap {
|
||||||
users[k] = madmin.UserInfo{
|
users[k] = madmin.UserInfo{
|
||||||
Status: madmin.AccountStatus(v.Status),
|
PolicyName: sys.iamPolicyMap[k],
|
||||||
|
Status: madmin.AccountStatus(v.Status),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,13 +334,13 @@ func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
if globalEtcdClient != nil {
|
||||||
if err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data); err != nil {
|
err = saveConfigEtcd(context.Background(), globalEtcdClient, configFile, data)
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if err = saveConfig(context.Background(), objectAPI, configFile, data); err != nil {
|
err = saveConfig(context.Background(), objectAPI, configFile, data)
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sys.Lock()
|
sys.Lock()
|
||||||
@ -332,8 +375,9 @@ func (sys *IAMSys) IsAllowed(args iampolicy.Args) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If policy is available for given user, check the policy.
|
// If policy is available for given user, check the policy.
|
||||||
if p, found := sys.iamPolicyMap[args.AccountName]; found {
|
if name, found := sys.iamPolicyMap[args.AccountName]; found {
|
||||||
return p.IsAllowed(args)
|
p, ok := sys.iamCannedPolicyMap[name]
|
||||||
|
return ok && p.IsAllowed(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// As policy is not available and OPA is not configured, return the owner value.
|
// As policy is not available and OPA is not configured, return the owner value.
|
||||||
@ -343,7 +387,7 @@ func (sys *IAMSys) IsAllowed(args iampolicy.Args) bool {
|
|||||||
var defaultContextTimeout = 5 * time.Minute
|
var defaultContextTimeout = 5 * time.Minute
|
||||||
|
|
||||||
// Similar to reloadUsers but updates users, policies maps from etcd server,
|
// Similar to reloadUsers but updates users, policies maps from etcd server,
|
||||||
func reloadEtcdUsers(prefix string, usersMap map[string]auth.Credentials, policyMap map[string]iampolicy.Policy) error {
|
func reloadEtcdUsers(prefix string, usersMap map[string]auth.Credentials, policyMap map[string]string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||||
r, err := globalEtcdClient.Get(ctx, prefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
r, err := globalEtcdClient.Get(ctx, prefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -400,18 +444,92 @@ func reloadEtcdUsers(prefix string, usersMap map[string]auth.Credentials, policy
|
|||||||
usersMap[cred.AccessKey] = cred
|
usersMap[cred.AccessKey] = cred
|
||||||
}
|
}
|
||||||
if perr == nil {
|
if perr == nil {
|
||||||
var p iampolicy.Policy
|
var policyName string
|
||||||
if err = json.Unmarshal(pdata, &p); err != nil {
|
if err = json.Unmarshal(pdata, &policyName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
policyMap[path.Base(prefix)] = p
|
policyMap[path.Base(prefix)] = policyName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reloadEtcdPolicies(prefix string, cannedPolicyMap map[string]iampolicy.Policy) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||||
|
r, err := globalEtcdClient.Get(ctx, prefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
||||||
|
defer cancel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// No users are created yet.
|
||||||
|
if r.Count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
policies := set.NewStringSet()
|
||||||
|
for _, kv := range r.Kvs {
|
||||||
|
// Extract policy by stripping off the `prefix` value as suffix,
|
||||||
|
// then strip off the remaining basename to obtain the prefix
|
||||||
|
// value, usually in the following form.
|
||||||
|
//
|
||||||
|
// key := "config/iam/policys/newpolicy/identity.json"
|
||||||
|
// prefix := "config/iam/policys/"
|
||||||
|
// v := trim(trim(key, prefix), base(key)) == "newpolicy"
|
||||||
|
//
|
||||||
|
policyName := strings.TrimSuffix(strings.TrimSuffix(string(kv.Key), prefix), path.Base(string(kv.Key)))
|
||||||
|
if !policies.Contains(policyName) {
|
||||||
|
policies.Add(policyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload config and policies for all policys.
|
||||||
|
for _, policyName := range policies.ToSlice() {
|
||||||
|
pFile := pathJoin(prefix, policyName, iamPolicyFile)
|
||||||
|
pdata, perr := readConfigEtcd(ctx, globalEtcdClient, pFile)
|
||||||
|
if perr != nil {
|
||||||
|
return perr
|
||||||
|
}
|
||||||
|
var p iampolicy.Policy
|
||||||
|
if err = json.Unmarshal(pdata, &p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cannedPolicyMap[path.Base(prefix)] = p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadPolicies(objectAPI ObjectLayer, prefix string, cannedPolicyMap map[string]iampolicy.Policy) error {
|
||||||
|
marker := ""
|
||||||
|
for {
|
||||||
|
var lo ListObjectsInfo
|
||||||
|
var err error
|
||||||
|
lo, err = objectAPI.ListObjects(context.Background(), minioMetaBucket, prefix, marker, "/", 1000)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
marker = lo.NextMarker
|
||||||
|
for _, prefix := range lo.Prefixes {
|
||||||
|
pFile := pathJoin(prefix, iamPolicyFile)
|
||||||
|
pdata, perr := readConfig(context.Background(), objectAPI, pFile)
|
||||||
|
if perr != nil {
|
||||||
|
return perr
|
||||||
|
}
|
||||||
|
var p iampolicy.Policy
|
||||||
|
if err = json.Unmarshal(pdata, &p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cannedPolicyMap[path.Base(prefix)] = p
|
||||||
|
}
|
||||||
|
if !lo.IsTruncated {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// reloadUsers reads an updates users, policies from object layer into user and policy maps.
|
// reloadUsers reads an updates users, policies from object layer into user and policy maps.
|
||||||
func reloadUsers(objectAPI ObjectLayer, prefix string, usersMap map[string]auth.Credentials, policyMap map[string]iampolicy.Policy) error {
|
func reloadUsers(objectAPI ObjectLayer, prefix string, usersMap map[string]auth.Credentials, policyMap map[string]string) error {
|
||||||
marker := ""
|
marker := ""
|
||||||
for {
|
for {
|
||||||
var lo ListObjectsInfo
|
var lo ListObjectsInfo
|
||||||
@ -451,11 +569,11 @@ func reloadUsers(objectAPI ObjectLayer, prefix string, usersMap map[string]auth.
|
|||||||
usersMap[cred.AccessKey] = cred
|
usersMap[cred.AccessKey] = cred
|
||||||
}
|
}
|
||||||
if perr == nil {
|
if perr == nil {
|
||||||
var p iampolicy.Policy
|
var policyName string
|
||||||
if err = json.Unmarshal(pdata, &p); err != nil {
|
if err = json.Unmarshal(pdata, &policyName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
policyMap[path.Base(prefix)] = p
|
policyMap[path.Base(prefix)] = policyName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !lo.IsTruncated {
|
if !lo.IsTruncated {
|
||||||
@ -468,9 +586,13 @@ func reloadUsers(objectAPI ObjectLayer, prefix string, usersMap map[string]auth.
|
|||||||
// Refresh IAMSys.
|
// Refresh IAMSys.
|
||||||
func (sys *IAMSys) refresh(objAPI ObjectLayer) error {
|
func (sys *IAMSys) refresh(objAPI ObjectLayer) error {
|
||||||
iamUsersMap := make(map[string]auth.Credentials)
|
iamUsersMap := make(map[string]auth.Credentials)
|
||||||
iamPolicyMap := make(map[string]iampolicy.Policy)
|
iamPolicyMap := make(map[string]string)
|
||||||
|
iamCannedPolicyMap := make(map[string]iampolicy.Policy)
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
if globalEtcdClient != nil {
|
||||||
|
if err := reloadEtcdPolicies(iamConfigPoliciesPrefix, iamCannedPolicyMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := reloadEtcdUsers(iamConfigUsersPrefix, iamUsersMap, iamPolicyMap); err != nil {
|
if err := reloadEtcdUsers(iamConfigUsersPrefix, iamUsersMap, iamPolicyMap); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -478,6 +600,9 @@ func (sys *IAMSys) refresh(objAPI ObjectLayer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if err := reloadPolicies(objAPI, iamConfigPoliciesPrefix, iamCannedPolicyMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := reloadUsers(objAPI, iamConfigUsersPrefix, iamUsersMap, iamPolicyMap); err != nil {
|
if err := reloadUsers(objAPI, iamConfigUsersPrefix, iamUsersMap, iamPolicyMap); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -491,6 +616,7 @@ func (sys *IAMSys) refresh(objAPI ObjectLayer) error {
|
|||||||
|
|
||||||
sys.iamUsersMap = iamUsersMap
|
sys.iamUsersMap = iamUsersMap
|
||||||
sys.iamPolicyMap = iamPolicyMap
|
sys.iamPolicyMap = iamPolicyMap
|
||||||
|
sys.iamCannedPolicyMap = iamCannedPolicyMap
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -498,7 +624,8 @@ func (sys *IAMSys) refresh(objAPI ObjectLayer) error {
|
|||||||
// NewIAMSys - creates new config system object.
|
// NewIAMSys - creates new config system object.
|
||||||
func NewIAMSys() *IAMSys {
|
func NewIAMSys() *IAMSys {
|
||||||
return &IAMSys{
|
return &IAMSys{
|
||||||
iamUsersMap: make(map[string]auth.Credentials),
|
iamUsersMap: make(map[string]auth.Credentials),
|
||||||
iamPolicyMap: make(map[string]iampolicy.Policy),
|
iamPolicyMap: make(map[string]string),
|
||||||
|
iamCannedPolicyMap: make(map[string]iampolicy.Policy),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,3 +76,9 @@ var errBucketAlreadyExists = errors.New("Your previous request to create the nam
|
|||||||
|
|
||||||
// error returned for a negative actual size.
|
// error returned for a negative actual size.
|
||||||
var errInvalidDecompressedSize = errors.New("Invalid Decompressed Size")
|
var errInvalidDecompressedSize = errors.New("Invalid Decompressed Size")
|
||||||
|
|
||||||
|
// error returned in IAM subsystem when user doesn't exist.
|
||||||
|
var errNoSuchUser = errors.New("Specified user does not exist")
|
||||||
|
|
||||||
|
// error returned in IAM subsystem when policy doesn't exist.
|
||||||
|
var errNoSuchPolicy = errors.New("Specified canned policy does not exist")
|
||||||
|
@ -9,13 +9,9 @@ In this document we will explain in detail on how to configure multiple users.
|
|||||||
- Install Minio - [Minio Quickstart Guide](https://docs.minio.io/docs/minio-quickstart-guide)
|
- Install Minio - [Minio Quickstart Guide](https://docs.minio.io/docs/minio-quickstart-guide)
|
||||||
|
|
||||||
### 2. Create a new user and policy
|
### 2. Create a new user and policy
|
||||||
Create a new user `newuser` on Minio use `mc admin users`, with a `newuser.json`.
|
Create new canned policy `getonly` with `newuser.json` use `mc admin policies`. This policy enables users to download all objects in my-bucketname.
|
||||||
```
|
|
||||||
mc admin users add myminio newuser newuser123 /tmp/newuser.json
|
|
||||||
```
|
|
||||||
|
|
||||||
An example user policy, enables `newuser` to download all objects in my-bucketname.
|
|
||||||
```json
|
```json
|
||||||
|
cat > getonly.json << EOF
|
||||||
{
|
{
|
||||||
"Version": "2012-10-17",
|
"Version": "2012-10-17",
|
||||||
"Statement": [
|
"Statement": [
|
||||||
@ -31,6 +27,14 @@ An example user policy, enables `newuser` to download all objects in my-bucketna
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mc admin policies add myminio getonly getonly.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new user `newuser` on Minio use `mc admin users`, additionally specify `getonly` canned policy for this `newuser`.
|
||||||
|
```
|
||||||
|
mc admin users add myminio newuser newuser123 getonly
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Revoke user
|
### 3. Revoke user
|
||||||
|
@ -38,10 +38,10 @@ func main() {
|
|||||||
|
|
||||||
| Service operations | Info operations | Healing operations | Config operations | IAM operations | Misc |
|
| Service operations | Info operations | Healing operations | Config operations | IAM operations | Misc |
|
||||||
|:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|:------------------------------------|
|
|:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|:------------------------------------|
|
||||||
| [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`AddUser()`](#AddUser) | [`SetAdminCredentials`](#SetAdminCredentials) |
|
| [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`AddUser`](#AddUser) | [`SetAdminCredentials`](#SetAdminCredentials) |
|
||||||
| [`ServiceSendAction`](#ServiceSendAction) | | | [`SetConfig`](#SetConfig) | [`AddUserPolicy`](#AddUserPolicy) | [`StartProfiling`](#StartProfiling) |
|
| [`ServiceSendAction`](#ServiceSendAction) | | | [`SetConfig`](#SetConfig) | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) |
|
||||||
| | | | [`GetConfigKeys`](#GetConfigKeys) | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) |
|
| | | | [`GetConfigKeys`](#GetConfigKeys) | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) |
|
||||||
| | | | [`SetConfigKeys`](#SetConfigKeys) | |
|
| | | | [`SetConfigKeys`](#SetConfigKeys) | [`AddCannedPolicy`](#AddCannedPolicy) | |
|
||||||
|
|
||||||
|
|
||||||
## 1. Constructor
|
## 1. Constructor
|
||||||
@ -349,6 +349,20 @@ __Example__
|
|||||||
|
|
||||||
## 8. IAM operations
|
## 8. IAM operations
|
||||||
|
|
||||||
|
<a name="AddCannedPolicy"></a>
|
||||||
|
### AddCannedPolicy(policyName string, policy string) error
|
||||||
|
Create a new canned policy on Minio server.
|
||||||
|
|
||||||
|
__Example__
|
||||||
|
|
||||||
|
```
|
||||||
|
policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}`
|
||||||
|
|
||||||
|
if err = madmClnt.AddCannedPolicy("get-only", policy); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<a name="AddUser"></a>
|
<a name="AddUser"></a>
|
||||||
### AddUser(user string, secret string) error
|
### AddUser(user string, secret string) error
|
||||||
Add a new user on a Minio server.
|
Add a new user on a Minio server.
|
||||||
@ -361,16 +375,14 @@ __Example__
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<a name="AddUserPolicy"></a>
|
<a name="SetUserPolicy"></a>
|
||||||
### AddUserPolicy(user string, policy string) error
|
### SetUserPolicy(user string, policyName string) error
|
||||||
Set a new policy for a given user on Minio server.
|
Enable a canned policy `get-only` for a given user on Minio server.
|
||||||
|
|
||||||
__Example__
|
__Example__
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}`
|
if err = madmClnt.SetUserPolicy("newuser", "get-only"); err != nil {
|
||||||
|
|
||||||
if err = madmClnt.AddUserPolicy("newuser", policy); err != nil {
|
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -46,7 +46,11 @@ func main() {
|
|||||||
// Create policy
|
// Create policy
|
||||||
policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}`
|
policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}`
|
||||||
|
|
||||||
if err = madmClnt.AddUserPolicy("newuser", policy); err != nil {
|
if err = madmClnt.AddCannedPolicy("get-only", policy); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = madmClnt.SetUserPolicy("newuser", "get-only"); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
pkg/madmin/policy-commands.go
Normal file
107
pkg/madmin/policy-commands.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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 madmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListCannedPolicies - list all configured canned policies.
|
||||||
|
func (adm *AdminClient) ListCannedPolicies() (map[string][]byte, error) {
|
||||||
|
reqData := requestData{
|
||||||
|
relPath: "/v1/list-canned-policies",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute GET on /minio/admin/v1/list-canned-policies
|
||||||
|
resp, err := adm.executeMethod("GET", reqData)
|
||||||
|
|
||||||
|
defer closeResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, httpRespToErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var policies = make(map[string][]byte)
|
||||||
|
if err = json.Unmarshal(respBytes, &policies); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return policies, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCannedPolicy - remove a policy for a canned.
|
||||||
|
func (adm *AdminClient) RemoveCannedPolicy(policyName string) error {
|
||||||
|
queryValues := url.Values{}
|
||||||
|
queryValues.Set("name", policyName)
|
||||||
|
|
||||||
|
reqData := requestData{
|
||||||
|
relPath: "/v1/remove-canned-policy",
|
||||||
|
queryValues: queryValues,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DELETE on /minio/admin/v1/remove-canned-policy to remove policy.
|
||||||
|
resp, err := adm.executeMethod("DELETE", reqData)
|
||||||
|
|
||||||
|
defer closeResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return httpRespToErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCannedPolicy - adds a policy for a canned.
|
||||||
|
func (adm *AdminClient) AddCannedPolicy(policyName, policy string) error {
|
||||||
|
queryValues := url.Values{}
|
||||||
|
queryValues.Set("name", policyName)
|
||||||
|
|
||||||
|
reqData := requestData{
|
||||||
|
relPath: "/v1/add-canned-policy",
|
||||||
|
queryValues: queryValues,
|
||||||
|
content: []byte(policy),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute PUT on /minio/admin/v1/add-canned-policy to set policy.
|
||||||
|
resp, err := adm.executeMethod("PUT", reqData)
|
||||||
|
|
||||||
|
defer closeResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return httpRespToErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -34,8 +34,9 @@ const (
|
|||||||
|
|
||||||
// UserInfo carries information about long term users.
|
// UserInfo carries information about long term users.
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
SecretKey string `json:"secretKey,omitempty"`
|
SecretKey string `json:"secretKey,omitempty"`
|
||||||
Status AccountStatus `json:"status"`
|
PolicyName string `json:"policyName,omitempty"`
|
||||||
|
Status AccountStatus `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUser - remove a user.
|
// RemoveUser - remove a user.
|
||||||
@ -137,43 +138,18 @@ func (adm *AdminClient) AddUser(accessKey, secretKey string) error {
|
|||||||
return adm.SetUser(accessKey, secretKey, AccountEnabled)
|
return adm.SetUser(accessKey, secretKey, AccountEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUserPolicy - remove a policy for a user.
|
// SetUserPolicy - adds a policy for a user.
|
||||||
func (adm *AdminClient) RemoveUserPolicy(accessKey string) error {
|
func (adm *AdminClient) SetUserPolicy(accessKey, policyName string) error {
|
||||||
queryValues := url.Values{}
|
queryValues := url.Values{}
|
||||||
queryValues.Set("accessKey", accessKey)
|
queryValues.Set("accessKey", accessKey)
|
||||||
|
queryValues.Set("name", policyName)
|
||||||
|
|
||||||
reqData := requestData{
|
reqData := requestData{
|
||||||
relPath: "/v1/remove-user-policy",
|
relPath: "/v1/set-user-policy",
|
||||||
queryValues: queryValues,
|
queryValues: queryValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute DELETE on /minio/admin/v1/remove-user-policy to remove policy.
|
// Execute PUT on /minio/admin/v1/set-user-policy to set policy.
|
||||||
resp, err := adm.executeMethod("DELETE", reqData)
|
|
||||||
|
|
||||||
defer closeResponse(resp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return httpRespToErrorResponse(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUserPolicy - adds a policy for a user.
|
|
||||||
func (adm *AdminClient) AddUserPolicy(accessKey, policy string) error {
|
|
||||||
queryValues := url.Values{}
|
|
||||||
queryValues.Set("accessKey", accessKey)
|
|
||||||
|
|
||||||
reqData := requestData{
|
|
||||||
relPath: "/v1/add-user-policy",
|
|
||||||
queryValues: queryValues,
|
|
||||||
content: []byte(policy),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute PUT on /minio/admin/v1/add-user-policy to set policy.
|
|
||||||
resp, err := adm.executeMethod("PUT", reqData)
|
resp, err := adm.executeMethod("PUT", reqData)
|
||||||
|
|
||||||
defer closeResponse(resp)
|
defer closeResponse(resp)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user