github.com/Axway/agent-sdk@v1.1.101/pkg/agent/handler/accessrequest.go (about)

     1  package handler
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	agentcache "github.com/Axway/agent-sdk/pkg/agent/cache"
     8  	apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
     9  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    10  	defs "github.com/Axway/agent-sdk/pkg/apic/definitions"
    11  	prov "github.com/Axway/agent-sdk/pkg/apic/provisioning"
    12  	"github.com/Axway/agent-sdk/pkg/util"
    13  	"github.com/Axway/agent-sdk/pkg/watchmanager/proto"
    14  )
    15  
    16  const (
    17  	provision   = "provision"
    18  	deprovision = "deprovision"
    19  	arFinalizer = "agent.accessrequest.provisioned"
    20  )
    21  
    22  type arProvisioner interface {
    23  	AccessRequestProvision(accessRequest prov.AccessRequest) (status prov.RequestStatus, data prov.AccessData)
    24  	AccessRequestDeprovision(accessRequest prov.AccessRequest) (status prov.RequestStatus)
    25  }
    26  
    27  type accessRequestHandler struct {
    28  	marketplaceHandler
    29  	prov          arProvisioner
    30  	cache         agentcache.Manager
    31  	client        client
    32  	encryptSchema encryptSchemaFunc
    33  }
    34  
    35  // NewAccessRequestHandler creates a Handler for Access Requests
    36  func NewAccessRequestHandler(prov arProvisioner, cache agentcache.Manager, client client) Handler {
    37  	return &accessRequestHandler{
    38  		prov:          prov,
    39  		cache:         cache,
    40  		client:        client,
    41  		encryptSchema: encryptSchema,
    42  	}
    43  }
    44  
    45  // Handle processes grpc events triggered for AccessRequests
    46  func (h *accessRequestHandler) Handle(ctx context.Context, meta *proto.EventMeta, resource *apiv1.ResourceInstance) error {
    47  	action := GetActionFromContext(ctx)
    48  	if resource.Kind != management.AccessRequestGVK().Kind || h.prov == nil || h.shouldIgnoreSubResourceUpdate(action, meta) {
    49  		return nil
    50  	}
    51  
    52  	log := getLoggerFromContext(ctx).WithComponent("accessRequestHandler")
    53  	defer log.Trace("finished processing request")
    54  	ctx = setLoggerInContext(ctx, log)
    55  
    56  	ar := &management.AccessRequest{}
    57  	err := ar.FromInstance(resource)
    58  	if err != nil {
    59  		log.WithError(err).Error("could not handle access request")
    60  		return nil
    61  	}
    62  
    63  	// add or update the cache with the access request
    64  	if action == proto.Event_CREATED || action == proto.Event_UPDATED {
    65  		h.cache.AddAccessRequest(resource)
    66  	}
    67  
    68  	if ok := isStatusFound(ar.Status); !ok {
    69  		log.Debug("could not handle access request as it did not have a status subresource")
    70  		return nil
    71  	}
    72  
    73  	if ok := h.shouldProcessPending(ar.Status, ar.Metadata.State); ok {
    74  		log.Trace("processing resource in pending status")
    75  		ar := h.onPending(ctx, ar)
    76  
    77  		ri, _ := ar.AsInstance()
    78  		defer h.cache.AddAccessRequest(ri)
    79  
    80  		err := h.client.CreateSubResource(ar.ResourceMeta, ar.SubResources)
    81  		if err != nil {
    82  			log.WithError(err).Error("error creating subresources")
    83  		}
    84  
    85  		// update the status regardless of errors updating the other subresources
    86  		statusErr := h.client.CreateSubResource(ar.ResourceMeta, map[string]interface{}{"status": ar.Status})
    87  		if statusErr != nil {
    88  			log.WithError(statusErr).Error("error creating status subresources")
    89  			return statusErr
    90  		}
    91  
    92  		return err
    93  	}
    94  
    95  	if ok := h.shouldProcessDeleting(ar.Status, ar.Metadata.State, ar.Finalizers); ok {
    96  		log.Trace("processing resource in deleting state")
    97  		h.onDeleting(ctx, ar)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (h *accessRequestHandler) onPending(ctx context.Context, ar *management.AccessRequest) *management.AccessRequest {
   104  	log := getLoggerFromContext(ctx)
   105  	app, err := h.getManagedApp(ctx, ar)
   106  	if err != nil {
   107  		log.WithError(err).Error("error getting managed app")
   108  		h.onError(ctx, ar, err)
   109  		return ar
   110  	}
   111  
   112  	// check the application status
   113  	if app.Status.Level != prov.Success.String() {
   114  		err = fmt.Errorf("error can't handle access request when application is not yet successful")
   115  		h.onError(ctx, ar, err)
   116  		return ar
   117  	}
   118  
   119  	ard, err := h.getARD(ctx, ar)
   120  	if err != nil {
   121  		log.WithError(err).Errorf("error getting access request definition")
   122  		h.onError(ctx, ar, err)
   123  		return ar
   124  	}
   125  
   126  	req, err := h.newReq(ctx, ar, util.GetAgentDetails(app))
   127  	if err != nil {
   128  		log.WithError(err).Error("error getting resource details")
   129  		h.onError(ctx, ar, err)
   130  		return ar
   131  	}
   132  
   133  	data := map[string]interface{}{}
   134  	status, accessData := h.prov.AccessRequestProvision(req)
   135  
   136  	if status.GetStatus() == prov.Success && accessData != nil {
   137  		sec := app.Spec.Security
   138  		d := accessData.GetData()
   139  		if ard.Spec.Provision == nil {
   140  			data = d // no provision schema found, return the data
   141  		} else if d != nil {
   142  			data, err = h.encryptSchema(
   143  				ard.Spec.Provision.Schema,
   144  				d,
   145  				sec.EncryptionKey, sec.EncryptionAlgorithm, sec.EncryptionHash,
   146  			)
   147  		}
   148  
   149  		if err != nil {
   150  			status = prov.NewRequestStatusBuilder().
   151  				SetMessage(fmt.Sprintf("error encrypting access data: %s", err.Error())).
   152  				SetCurrentStatusReasons(ar.Status.Reasons).
   153  				Failed()
   154  		}
   155  	}
   156  
   157  	ar.Data = data
   158  	ar.Status = prov.NewStatusReason(status)
   159  
   160  	details := util.MergeMapStringString(util.GetAgentDetailStrings(ar), status.GetProperties())
   161  	util.SetAgentDetails(ar, util.MapStringStringToMapStringInterface(details))
   162  
   163  	ri, _ := ar.AsInstance()
   164  	if ar.Status.Level == prov.Success.String() {
   165  		// only add finalizer on success
   166  		h.client.UpdateResourceFinalizer(ri, arFinalizer, "", true)
   167  	}
   168  
   169  	ar.SubResources = map[string]interface{}{
   170  		defs.XAgentDetails: util.GetAgentDetails(ar),
   171  		"data":             ar.Data,
   172  	}
   173  
   174  	return ar
   175  }
   176  
   177  // onError updates the AccessRequest with an error status
   178  func (h *accessRequestHandler) onError(_ context.Context, ar *management.AccessRequest, err error) {
   179  	ps := prov.NewRequestStatusBuilder()
   180  	status := ps.SetMessage(err.Error()).SetCurrentStatusReasons(ar.Status.Reasons).Failed()
   181  	ar.Status = prov.NewStatusReason(status)
   182  	ar.SubResources = map[string]interface{}{
   183  		"status": ar.Status,
   184  	}
   185  }
   186  
   187  // onDeleting deprovisions an access request and removes the finalizer
   188  func (h *accessRequestHandler) onDeleting(ctx context.Context, ar *management.AccessRequest) {
   189  	log := getLoggerFromContext(ctx)
   190  
   191  	app, err := h.getManagedApp(ctx, ar)
   192  	if err != nil {
   193  		log.WithError(err).Error("error getting managed app")
   194  		h.onError(ctx, ar, err)
   195  		return
   196  	}
   197  
   198  	ri, _ := ar.AsInstance()
   199  
   200  	req, err := h.newReq(ctx, ar, util.GetAgentDetails(app))
   201  	if err != nil {
   202  		log.WithError(err).Debug("removing finalizers on the access request")
   203  		h.client.UpdateResourceFinalizer(ri, arFinalizer, "", false)
   204  		h.cache.DeleteAccessRequest(ri.Metadata.ID)
   205  		return
   206  	}
   207  
   208  	status := h.prov.AccessRequestDeprovision(req)
   209  
   210  	if status.GetStatus() == prov.Success || err != nil {
   211  		h.client.UpdateResourceFinalizer(ri, arFinalizer, "", false)
   212  		h.cache.DeleteAccessRequest(ri.Metadata.ID)
   213  	} else {
   214  		err := fmt.Errorf(status.GetMessage())
   215  		log.WithError(err).Error("request status was not Success, skipping")
   216  		h.onError(ctx, ar, fmt.Errorf(status.GetMessage()))
   217  		h.client.CreateSubResource(ar.ResourceMeta, ar.SubResources)
   218  	}
   219  }
   220  
   221  func (h *accessRequestHandler) getManagedApp(_ context.Context, ar *management.AccessRequest) (*management.ManagedApplication, error) {
   222  	app := management.NewManagedApplication(ar.Spec.ManagedApplication, ar.Metadata.Scope.Name)
   223  	ri, err := h.client.GetResource(app.GetSelfLink())
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	app = &management.ManagedApplication{}
   229  	err = app.FromInstance(ri)
   230  	return app, err
   231  }
   232  
   233  func (h *accessRequestHandler) getARD(ctx context.Context, ar *management.AccessRequest) (*management.AccessRequestDefinition, error) {
   234  	// get the instance from the cache
   235  	instance, err := h.getServiceInstance(ctx, ar)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	svcInst := management.NewAPIServiceInstance(instance.Name, instance.Metadata.Scope.Name)
   240  
   241  	err = svcInst.FromInstance(instance)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	// verify that the service instance has an access request definition
   246  	if svcInst.Spec.AccessRequestDefinition == "" {
   247  		return nil, fmt.Errorf("failed to provision access for service instance %s. Please contact your system administrator for further assistance", svcInst.Name)
   248  	}
   249  
   250  	// now get the access request definition from the instance
   251  	ard := management.NewAccessRequestDefinition(svcInst.Spec.AccessRequestDefinition, ar.Metadata.Scope.Name)
   252  
   253  	ri, err := h.client.GetResource(ard.GetSelfLink())
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	ard = &management.AccessRequestDefinition{}
   259  	err = ard.FromInstance(ri)
   260  	return ard, err
   261  }
   262  
   263  func (h *accessRequestHandler) getServiceInstance(_ context.Context, ar *management.AccessRequest) (*apiv1.ResourceInstance, error) {
   264  	instRef := ar.GetReferenceByGVK(management.APIServiceInstanceGVK())
   265  	instID := instRef.ID
   266  	instance, err := h.cache.GetAPIServiceInstanceByID(instID)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	return instance, nil
   271  }
   272  
   273  func (h *accessRequestHandler) newReq(ctx context.Context, ar *management.AccessRequest, appDetails map[string]interface{}) (*provAccReq, error) {
   274  	instance, err := h.getServiceInstance(ctx, ar)
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  
   279  	return &provAccReq{
   280  		appDetails:      appDetails,
   281  		requestData:     ar.Spec.Data,
   282  		provData:        ar.Data,
   283  		accessDetails:   util.GetAgentDetails(ar),
   284  		instanceDetails: util.GetAgentDetails(instance),
   285  		managedApp:      ar.Spec.ManagedApplication,
   286  		id:              ar.Metadata.ID,
   287  		quota:           prov.NewQuotaFromAccessRequest(ar),
   288  	}, nil
   289  }
   290  
   291  type provAccReq struct {
   292  	appDetails      map[string]interface{}
   293  	accessDetails   map[string]interface{}
   294  	requestData     map[string]interface{}
   295  	instanceDetails map[string]interface{}
   296  	provData        interface{}
   297  	managedApp      string
   298  	id              string
   299  	quota           prov.Quota
   300  }
   301  
   302  // GetApplicationName gets the application name the access request is linked too.
   303  func (r provAccReq) GetApplicationName() string {
   304  	return r.managedApp
   305  }
   306  
   307  // GetID gets the if of the access request resource
   308  func (r provAccReq) GetID() string {
   309  	return r.id
   310  }
   311  
   312  // GetAccessRequestData gets the data of the access request
   313  func (r provAccReq) GetAccessRequestData() map[string]interface{} {
   314  	return r.requestData
   315  }
   316  
   317  // GetAccessRequestData gets the data of the access request
   318  func (r provAccReq) GetAccessRequestProvisioningData() interface{} {
   319  	return r.provData
   320  }
   321  
   322  // GetApplicationDetailsValue returns a value found on the 'x-agent-details' sub resource of the ManagedApplication.
   323  func (r provAccReq) GetApplicationDetailsValue(key string) string {
   324  	if r.appDetails == nil {
   325  		return ""
   326  	}
   327  
   328  	return util.ToString(r.appDetails[key])
   329  }
   330  
   331  // GetAccessRequestDetailsValue returns a value found on the 'x-agent-details' sub resource of the AccessRequest.
   332  func (r provAccReq) GetAccessRequestDetailsValue(key string) string {
   333  	if r.accessDetails == nil {
   334  		return ""
   335  	}
   336  
   337  	return util.ToString(r.accessDetails[key])
   338  }
   339  
   340  // GetInstanceDetails returns the 'x-agent-details' sub resource of the API Service Instance
   341  func (r provAccReq) GetInstanceDetails() map[string]interface{} {
   342  	if r.instanceDetails == nil {
   343  		return map[string]interface{}{}
   344  	}
   345  
   346  	return r.instanceDetails
   347  }
   348  
   349  func (r provAccReq) GetQuota() prov.Quota {
   350  	return r.quota
   351  }