2018-06-11 13:13:19 +00:00
package resourcecontrols
import (
2019-11-12 23:41:42 +00:00
"errors"
2018-06-11 13:13:19 +00:00
"net/http"
"github.com/asaskevich/govalidator"
2018-09-10 10:01:38 +00:00
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
2021-02-23 03:21:39 +00:00
portainer "github.com/portainer/portainer/api"
2018-06-11 13:13:19 +00:00
)
type resourceControlCreatePayload struct {
2021-02-23 03:21:39 +00:00
//
ResourceID string ` example:"617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" validate:"required" `
// Type of Docker resource. Valid values are: container, volume\
// service, secret, config or stack
Type string ` example:"container" validate:"required" `
// Permit access to the associated resource to any user
Public bool ` example:"true" `
// Permit access to resource only to admins
AdministratorsOnly bool ` example:"true" `
// List of user identifiers with access to the associated resource
Users [ ] int ` example:"1,4" `
// List of team identifiers with access to the associated resource
Teams [ ] int ` example:"56,7" `
// List of Docker resources that will inherit this access control
SubResourceIDs [ ] string ` example:"617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" `
2018-06-11 13:13:19 +00:00
}
2020-07-07 21:57:52 +00:00
var (
errResourceControlAlreadyExists = errors . New ( "A resource control is already applied on this resource" ) //http/resourceControl
errInvalidResourceControlType = errors . New ( "Unsupported resource control type" ) //http/resourceControl
)
2018-06-11 13:13:19 +00:00
func ( payload * resourceControlCreatePayload ) Validate ( r * http . Request ) error {
if govalidator . IsNull ( payload . ResourceID ) {
2019-11-12 23:41:42 +00:00
return errors . New ( "invalid payload: invalid resource identifier" )
2018-06-11 13:13:19 +00:00
}
if govalidator . IsNull ( payload . Type ) {
2019-11-12 23:41:42 +00:00
return errors . New ( "invalid payload: invalid type" )
2018-06-11 13:13:19 +00:00
}
2019-11-12 23:41:42 +00:00
if len ( payload . Users ) == 0 && len ( payload . Teams ) == 0 && ! payload . Public && ! payload . AdministratorsOnly {
return errors . New ( "invalid payload: must specify Users, Teams, Public or AdministratorsOnly" )
}
if payload . Public && payload . AdministratorsOnly {
return errors . New ( "invalid payload: cannot set both public and administrators only flags to true" )
2018-06-11 13:13:19 +00:00
}
return nil
}
2021-02-23 03:21:39 +00:00
// @id ResourceControlCreate
// @summary Create a new resource control
// @description Create a new resource control to restrict access to a Docker resource.
// @description **Access policy**: administrator
// @tags resource_controls
// @security jwt
// @accept json
// @produce json
// @param body body resourceControlCreatePayload true "Resource control details"
// @success 200 {object} portainer.ResourceControl "Success"
// @failure 400 "Invalid request"
// @failure 409 "Resource control already exists"
// @failure 500 "Server error"
// @router /resource_controls [post]
2018-06-11 13:13:19 +00:00
func ( handler * Handler ) resourceControlCreate ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
var payload resourceControlCreatePayload
err := request . DecodeAndValidateJSONPayload ( r , & payload )
if err != nil {
return & httperror . HandlerError { http . StatusBadRequest , "Invalid request payload" , err }
}
var resourceControlType portainer . ResourceControlType
switch payload . Type {
case "container" :
resourceControlType = portainer . ContainerResourceControl
2021-03-29 21:58:56 +00:00
case "container-group" :
resourceControlType = portainer . ContainerGroupResourceControl
2018-06-11 13:13:19 +00:00
case "service" :
resourceControlType = portainer . ServiceResourceControl
case "volume" :
resourceControlType = portainer . VolumeResourceControl
case "network" :
resourceControlType = portainer . NetworkResourceControl
case "secret" :
resourceControlType = portainer . SecretResourceControl
case "stack" :
resourceControlType = portainer . StackResourceControl
case "config" :
resourceControlType = portainer . ConfigResourceControl
default :
2020-07-07 21:57:52 +00:00
return & httperror . HandlerError { http . StatusBadRequest , "Invalid type value. Value must be one of: container, service, volume, network, secret, stack or config" , errInvalidResourceControlType }
2018-06-11 13:13:19 +00:00
}
2020-05-20 05:23:15 +00:00
rc , err := handler . DataStore . ResourceControl ( ) . ResourceControlByResourceIDAndType ( payload . ResourceID , resourceControlType )
2019-11-12 23:41:42 +00:00
if err != nil {
2018-06-11 13:13:19 +00:00
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve resource controls from the database" , err }
}
if rc != nil {
2020-07-07 21:57:52 +00:00
return & httperror . HandlerError { http . StatusConflict , "A resource control is already associated to this resource" , errResourceControlAlreadyExists }
2018-06-11 13:13:19 +00:00
}
var userAccesses = make ( [ ] portainer . UserResourceAccess , 0 )
for _ , v := range payload . Users {
userAccess := portainer . UserResourceAccess {
UserID : portainer . UserID ( v ) ,
AccessLevel : portainer . ReadWriteAccessLevel ,
}
userAccesses = append ( userAccesses , userAccess )
}
var teamAccesses = make ( [ ] portainer . TeamResourceAccess , 0 )
for _ , v := range payload . Teams {
teamAccess := portainer . TeamResourceAccess {
TeamID : portainer . TeamID ( v ) ,
AccessLevel : portainer . ReadWriteAccessLevel ,
}
teamAccesses = append ( teamAccesses , teamAccess )
}
resourceControl := portainer . ResourceControl {
2019-11-12 23:41:42 +00:00
ResourceID : payload . ResourceID ,
SubResourceIDs : payload . SubResourceIDs ,
Type : resourceControlType ,
Public : payload . Public ,
AdministratorsOnly : payload . AdministratorsOnly ,
UserAccesses : userAccesses ,
TeamAccesses : teamAccesses ,
2018-06-11 13:13:19 +00:00
}
2020-05-20 05:23:15 +00:00
err = handler . DataStore . ResourceControl ( ) . CreateResourceControl ( & resourceControl )
2018-06-11 13:13:19 +00:00
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to persist the resource control inside the database" , err }
}
return response . JSON ( w , resourceControl )
}