// authAndActionFromTagFn first authenticates the request, and then returns // a function with which to authenticate and retrieve each action in the // request. func (u *UniterAPIV3) authAndActionFromTagFn() (func(string) (*state.Action, error), error) { canAccess, err := u.accessUnit() if err != nil { return nil, err } unit, ok := u.auth.GetAuthTag().(names.UnitTag) if !ok { return nil, fmt.Errorf("calling entity is not a unit") } return func(tag string) (*state.Action, error) { actionTag, err := names.ParseActionTag(tag) if err != nil { return nil, err } action, err := u.st.ActionByTag(actionTag) if err != nil { return nil, err } receiverTag, err := names.ActionReceiverTag(action.Receiver()) if err != nil { return nil, err } if unit != receiverTag { return nil, common.ErrPerm } if !canAccess(receiverTag) { return nil, common.ErrPerm } return action, nil }, nil }
// Actions takes a list of ActionTags, and returns the full Action for // each ID. func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) { response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} for i, entity := range arg.Entities { currentResult := &response.Results[i] tag, err := names.ParseTag(entity.Tag) if err != nil { currentResult.Error = common.ServerError(common.ErrBadId) continue } actionTag, ok := tag.(names.ActionTag) if !ok { currentResult.Error = common.ServerError(common.ErrBadId) continue } action, err := a.state.ActionByTag(actionTag) if err != nil { currentResult.Error = common.ServerError(common.ErrBadId) continue } receiverTag, err := names.ActionReceiverTag(action.Receiver()) if err != nil { currentResult.Error = common.ServerError(err) continue } response.Results[i] = common.MakeActionResult(receiverTag, action) } return response, nil }
// Cancel attempts to cancel enqueued Actions from running. func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) { response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} for i, entity := range arg.Entities { currentResult := &response.Results[i] tag, err := names.ParseTag(entity.Tag) if err != nil { currentResult.Error = common.ServerError(common.ErrBadId) continue } actionTag, ok := tag.(names.ActionTag) if !ok { currentResult.Error = common.ServerError(common.ErrBadId) continue } action, err := a.state.ActionByTag(actionTag) if err != nil { currentResult.Error = common.ServerError(err) continue } result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"}) if err != nil { currentResult.Error = common.ServerError(err) continue } receiverTag, err := names.ActionReceiverTag(result.Receiver()) if err != nil { currentResult.Error = common.ServerError(err) continue } response.Results[i] = makeActionResult(receiverTag, result) } return response, nil }
// AuthAndActionFromTagFn takes in an authorizer function and a function that can fetch action by tags from state // and returns a function that can fetch an action from state by id and check the authorization. func AuthAndActionFromTagFn(canAccess AuthFunc, getActionByTag func(names.ActionTag) (state.Action, error)) func(string) (state.Action, error) { return func(tag string) (state.Action, error) { actionTag, err := names.ParseActionTag(tag) if err != nil { return nil, errors.Trace(err) } action, err := getActionByTag(actionTag) if err != nil { return nil, errors.Trace(err) } receiverTag, err := names.ActionReceiverTag(action.Receiver()) if err != nil { return nil, errors.Trace(err) } if !canAccess(receiverTag) { return nil, ErrPerm } return action, nil } }
func (s *actionSuite) TestActionReceiverTag(c *gc.C) { testCases := []struct { name string expected names.Tag valid bool }{ {name: "mysql", valid: false}, {name: "mysql/3", expected: names.NewUnitTag("mysql/3"), valid: true}, } for _, tcase := range testCases { tag, err := names.ActionReceiverTag(tcase.name) c.Check(err == nil, gc.Equals, tcase.valid) if err != nil { continue } c.Check(tag, gc.FitsTypeOf, tcase.expected) c.Check(tag, gc.Equals, tcase.expected) } }
func (a *ActionAPI) FindActionsByNames(arg params.FindActionsByNames) (params.ActionsByNames, error) { response := params.ActionsByNames{Actions: make([]params.ActionsByName, len(arg.ActionNames))} for i, name := range arg.ActionNames { currentResult := &response.Actions[i] currentResult.Name = name actions, err := a.state.FindActionsByName(name) if err != nil { currentResult.Error = common.ServerError(err) continue } for _, action := range actions { recvTag, err := names.ActionReceiverTag(action.Receiver()) if err != nil { currentResult.Actions = append(currentResult.Actions, params.ActionResult{Error: common.ServerError(err)}) continue } currentAction := common.MakeActionResult(recvTag, action) currentResult.Actions = append(currentResult.Actions, currentAction) } } return response, nil }
func (s *actionSuite) TestActionReceiverTag(c *gc.C) { testCases := []struct { name string expected names.Tag err string }{ {name: "mysql", err: `invalid actionreceiver name "mysql"`}, {name: "mysql/3", expected: names.NewUnitTag("mysql/3")}, {name: "3", expected: names.NewMachineTag("3")}, } for _, tcase := range testCases { tag, err := names.ActionReceiverTag(tcase.name) if tcase.err != "" { c.Check(err, gc.ErrorMatches, tcase.err) c.Check(tag, gc.IsNil) continue } c.Check(err, jc.ErrorIsNil) c.Check(tag, gc.FitsTypeOf, tcase.expected) c.Check(tag, gc.Equals, tcase.expected) } }