launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/apiserver/firewaller/firewaller.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  package firewaller
     4  
     5  import (
     6  	"launchpad.net/errgo/errors"
     7  	"launchpad.net/juju-core/names"
     8  	"launchpad.net/juju-core/state"
     9  	"launchpad.net/juju-core/state/api/params"
    10  	"launchpad.net/juju-core/state/apiserver/common"
    11  )
    12  
    13  var mask = errors.Mask
    14  
    15  // FirewallerAPI provides access to the Firewaller API facade.
    16  type FirewallerAPI struct {
    17  	*common.LifeGetter
    18  	*common.EnvironWatcher
    19  	*common.AgentEntityWatcher
    20  	*common.UnitsWatcher
    21  	*common.EnvironMachinesWatcher
    22  	*common.InstanceIdGetter
    23  
    24  	st            *state.State
    25  	resources     *common.Resources
    26  	authorizer    common.Authorizer
    27  	accessUnit    common.GetAuthFunc
    28  	accessService common.GetAuthFunc
    29  }
    30  
    31  // NewFirewallerAPI creates a new server-side FirewallerAPI facade.
    32  func NewFirewallerAPI(
    33  	st *state.State,
    34  	resources *common.Resources,
    35  	authorizer common.Authorizer,
    36  ) (*FirewallerAPI, error) {
    37  	if !authorizer.AuthEnvironManager() {
    38  		// Firewaller must run as environment manager.
    39  		return nil, common.ErrPerm
    40  	}
    41  	// Set up the various authorization checkers.
    42  	accessUnit := getAuthFuncForTagKind(names.UnitTagKind)
    43  	accessService := getAuthFuncForTagKind(names.ServiceTagKind)
    44  	accessMachine := getAuthFuncForTagKind(names.MachineTagKind)
    45  	accessEnviron := getAuthFuncForTagKind("")
    46  	accessUnitOrService := common.AuthEither(accessUnit, accessService)
    47  	accessUnitServiceOrMachine := common.AuthEither(accessUnitOrService, accessMachine)
    48  
    49  	// Life() is supported for units, services or machines.
    50  	lifeGetter := common.NewLifeGetter(
    51  		st,
    52  		accessUnitServiceOrMachine,
    53  	)
    54  	// EnvironConfig() and WatchForEnvironConfigChanges() are allowed
    55  	// with unrestriced access.
    56  	environWatcher := common.NewEnvironWatcher(
    57  		st,
    58  		resources,
    59  		accessEnviron,
    60  		accessEnviron,
    61  	)
    62  	// Watch() is supported for units or services.
    63  	entityWatcher := common.NewAgentEntityWatcher(
    64  		st,
    65  		resources,
    66  		accessUnitOrService,
    67  	)
    68  	// WatchUnits() is supported for machines.
    69  	unitsWatcher := common.NewUnitsWatcher(st,
    70  		resources,
    71  		accessMachine,
    72  	)
    73  	// WatchEnvironMachines() is allowed with unrestricted access.
    74  	machinesWatcher := common.NewEnvironMachinesWatcher(
    75  		st,
    76  		resources,
    77  		accessEnviron,
    78  	)
    79  	// InstanceId() is supported for machines.
    80  	instanceIdGetter := common.NewInstanceIdGetter(
    81  		st,
    82  		accessMachine,
    83  	)
    84  	return &FirewallerAPI{
    85  		LifeGetter:             lifeGetter,
    86  		EnvironWatcher:         environWatcher,
    87  		AgentEntityWatcher:     entityWatcher,
    88  		UnitsWatcher:           unitsWatcher,
    89  		EnvironMachinesWatcher: machinesWatcher,
    90  		InstanceIdGetter:       instanceIdGetter,
    91  		st:                     st,
    92  		resources:              resources,
    93  		authorizer:             authorizer,
    94  		accessUnit:             accessUnit,
    95  		accessService:          accessService,
    96  	}, nil
    97  }
    98  
    99  // OpenedPorts returns the list of opened ports for each given unit.
   100  func (f *FirewallerAPI) OpenedPorts(args params.Entities) (params.PortsResults, error) {
   101  	result := params.PortsResults{
   102  		Results: make([]params.PortsResult, len(args.Entities)),
   103  	}
   104  	canAccess, err := f.accessUnit()
   105  	if err != nil {
   106  		return params.PortsResults{}, mask(err)
   107  	}
   108  	for i, entity := range args.Entities {
   109  		var unit *state.Unit
   110  		unit, err = f.getUnit(canAccess, entity.Tag)
   111  		if err == nil {
   112  			result.Results[i].Ports = unit.OpenedPorts()
   113  		}
   114  		result.Results[i].Error = common.ServerError(err)
   115  	}
   116  	return result, nil
   117  }
   118  
   119  // GetExposed returns the exposed flag value for each given service.
   120  func (f *FirewallerAPI) GetExposed(args params.Entities) (params.BoolResults, error) {
   121  	result := params.BoolResults{
   122  		Results: make([]params.BoolResult, len(args.Entities)),
   123  	}
   124  	canAccess, err := f.accessService()
   125  	if err != nil {
   126  		return params.BoolResults{}, mask(err)
   127  	}
   128  	for i, entity := range args.Entities {
   129  		var service *state.Service
   130  		service, err = f.getService(canAccess, entity.Tag)
   131  		if err == nil {
   132  			result.Results[i].Result = service.IsExposed()
   133  		}
   134  		result.Results[i].Error = common.ServerError(err)
   135  	}
   136  	return result, nil
   137  }
   138  
   139  // GetAssignedMachine returns the assigned machine tag (if any) for
   140  // each given unit.
   141  func (f *FirewallerAPI) GetAssignedMachine(args params.Entities) (params.StringResults, error) {
   142  	result := params.StringResults{
   143  		Results: make([]params.StringResult, len(args.Entities)),
   144  	}
   145  	canAccess, err := f.accessUnit()
   146  	if err != nil {
   147  		return params.StringResults{}, mask(err)
   148  	}
   149  	for i, entity := range args.Entities {
   150  		var unit *state.Unit
   151  		unit, err = f.getUnit(canAccess, entity.Tag)
   152  		if err == nil {
   153  			var machineId string
   154  			machineId, err = unit.AssignedMachineId()
   155  			if err == nil {
   156  				result.Results[i].Result = names.MachineTag(machineId)
   157  			}
   158  		}
   159  		result.Results[i].Error = common.ServerError(err)
   160  	}
   161  	return result, nil
   162  }
   163  
   164  func (f *FirewallerAPI) getEntity(canAccess common.AuthFunc, tag string) (state.Entity, error) {
   165  	if !canAccess(tag) {
   166  		return nil, common.ErrPerm
   167  	}
   168  	return f.st.FindEntity(tag)
   169  }
   170  
   171  func (f *FirewallerAPI) getUnit(canAccess common.AuthFunc, tag string) (*state.Unit, error) {
   172  	entity, err := f.getEntity(canAccess, tag)
   173  	if err != nil {
   174  		// The authorization function guarantees that the tag represents a
   175  		// unit.
   176  		return nil, mask(err, errors.Any)
   177  	}
   178  
   179  	return entity.(*state.Unit), nil
   180  }
   181  
   182  func (f *FirewallerAPI) getService(canAccess common.AuthFunc, tag string) (*state.Service, error) {
   183  	entity, err := f.getEntity(canAccess, tag)
   184  	if err != nil {
   185  		// The authorization function guarantees that the tag represents a
   186  		// service.
   187  		return nil, mask(err, errors.Any)
   188  	}
   189  
   190  	return entity.(*state.Service), nil
   191  }
   192  
   193  // getAuthFuncForTagKind returns a GetAuthFunc which creates an
   194  // AuthFunc allowing only the given tag kind and denies all
   195  // others. In the special case where a single empty string is given,
   196  // it's assumed only environment tags are allowed, but not specified
   197  // (for now).
   198  func getAuthFuncForTagKind(kind string) common.GetAuthFunc {
   199  	return func() (common.AuthFunc, error) {
   200  		return func(tag string) bool {
   201  			if tag == "" {
   202  				// Assume an empty tag means a missing environment tag.
   203  				return kind == ""
   204  			}
   205  			// Allow only the given tag kind.
   206  			_, _, err := names.ParseTag(tag, kind)
   207  			if err != nil {
   208  				return false
   209  			}
   210  			return true
   211  		}, nil
   212  	}
   213  }