Property Permission: Evaluate permissions for updated object instead of persisted object?

Hi there,

I’m playing around with Property Permission and got an issue that before checking properties, the UAA backend evaluates permissions on updatedObject to get all rights passed.

I have 3 use cases on an A12 document data:

  • Document data has a contract-status field that is neither policy-created nor offer-created
  • Document data has a contract-status field that is offer-created
  • Document data has a contract-status field that is policy-created

I created 3 policies for 3 of those cases:

{
	"name": "Dev-dpos Additional Authorization Definition",
	"description": "",
	"policies": [
		{
			"name": "Contract on offer-created status",
			"description": "Check the contract status to allow users to update certain fields",
			"target": "#resource instanceof T(com.mgmtp.a12.kernel.md.document.api.IDocument)",
			"rules": [
				"@dposDocumentResolver.getValueForAnnotatedField(#resource, 'contract-status') == 'offer-created'"
			]
		},
		{
			"name": "Contract on policy-created status",
			"description": "Check the contract status to allow users to update certain fields",
			"target": "#resource instanceof T(com.mgmtp.a12.kernel.md.document.api.IDocument)",
			"rules": [
				"@dposDocumentResolver.getValueForAnnotatedField(#resource, 'contract-status') == 'policy-created'"
			]
		},
		{
			"name": "Contract is not on offer-created or policy-created status",
			"description": "users allowed to modify whole document",
			"target": "#resource instanceof T(com.mgmtp.a12.kernel.md.document.api.IDocument)",
			"rules": [
				"@dposDocumentResolver.getValueForAnnotatedField(#resource, 'contract-status') != 'offer-created'",
				"@dposDocumentResolver.getValueForAnnotatedField(#resource, 'contract-status') != 'policy-created'"
			]
		},
		{
			"name": "Is Contract Document",
			"description": "",
			"target": "#resource instanceof T(com.mgmtp.a12.kernel.md.document.api.IDocument)",
			"rules": [
				"#resource.getDocumentModelId() == 'DnoContract' || #resource.getDocumentModelId() == 'DnoContract_CDM'"
			]
		}
	],
	"propertyPermissions": [
		{
			"name": "Property rights per: User can only update partially field on offer-created status",
			"description": "",
			"policy-refs": [
				"Is Contract Document",
				"Contract on offer-created status"
			],
			"rights-refs": [
				"Property rights def: User can only update partially field on offer-created status"
			]
		},
		{
			"name": "Property rights per: User can not update offer on policy-created status",
			"description": "",
			"policy-refs": [
				"Is Contract Document",
				"Contract on policy-created status"
			],
			"rights-refs": [
				"Property rights def: User can not update offer on policy-created status"
			]
		},
		{
			"name": "Property rights per: Users can access Contract Document",
			"description": "",
			"policy-refs": [
				"Is Contract Document",
				"Contract is not on offer-created or policy-created status"
			],
			"rights-refs": [
				"Property rights def: Users can access Contract Document"
			]
		}
	],
	"propertyRights": []
}

And my expectation would be:

  • If Persisted Document Data is neither policy-created nor offer-created, the rights Property rights def: Users can access Contract Document will be executed.
  • If Persisted Document Data is policy-created, the rights Property rights def: User can not update offer on policy-created status will be executed.
  • If Persisted Document Data is offer-created, the rights `Property rights def: User can only update partially field on offer-created status will be executed.

But the other way around is used in UAA workflow:

  • If Updated Document Data is neither policy-created nor offer-created, the rights Property rights def: Users can access Contract Document will be executed.
  • If Updated Document Data is policy-created, the rights Property rights def: User can not update offer on policy-created status will be executed. => if the persisted document has a status policy-created, the document data should be read-only. Sadly in this case, if the user executes modify document w/o field contract-status then they will be able to update the whole document. This is not correct in our business.
  • If Updated Document Data is offer-created, the rights `Property rights def: User can only update partially field on offer-created status will be executed.

Am I misunderstanding the Property Permissions workflow?

# A12 versions
a12BaseVersion=27.1.0
a12UaaVersion=7.5.1
a12KernelVersion=28.5.1
a12FormEngineVersion=36.7.2
a12DataServicesVersion=36.3.7

Thanks in advance!

Best, Nhat

After talking with Tuan from @UAA,
The UpdatedDocument is passed to the security context to execute the propertyPermissions based on user roles as designed correctly.

In our case, we want the propertyPermissions to be based on data state which is not supported by UAA yet.

I added dataPreload to the policy to check the persisted document state and it works as expected, see below:

		{
			"name": "Contract is not on offer-created or policy-created status",
			"description": "Check the contract status to allow users update certain fields",
			"target": "#resource instanceof T(com.mgmtp.a12.kernel.md.document.api.IDocument)",
			"dataPreload": [
				"#persistedDocument = @dposDocumentResolver.getPersistedDocument(#resource)"
			],
			"rules": [
				"@dposDocumentResolver.getValueForAnnotatedField(#persistedDocument, 'contract-status') != 'offer-created'",
				"@dposDocumentResolver.getValueForAnnotatedField(#persistedDocument, 'contract-status') != 'policy-created'"
			]
		}

The Document ID needs to be set into IDocument because it’s not filled in this case:

	@DataServicesEventListener
	public void beforeUpdateEvent(final DocumentBeforeUpdateEvent event) {
		if (enabledForModels.contains(event.getPersistedDocument().getDocumentModelId())) {
			IDocument persistedDocument = event.getPersistedDocument();
			persistedDocument.setId(event.getDocumentReference().getDocumentId());

			IDocument updatedDocument = event.getUpdatedDocument();
			updatedDocument.setId(event.getDocumentReference().getDocumentId());
			if (!authorizationService.checkPropertyPermissionsForChanges(persistedDocument, updatedDocument)) {
				throw new AccessDeniedException("Property permissions: Access Denied!");
			}
		}
	}

=> The consequence here the document is loaded twice.

Thank you a @Tuan!

Best, Nhat