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 }