Caché Classes: How to Make a View on a Class to Manage Property Level Security?
Hi, Community!
Suppose I have class A with properties P1 and P2.
I want to introduce class B, which would have same records as Class A, but only one property - P2.
What is the easiest way to manage it assuming that I would like to use Class A to add records and be available for any operations to Users with Role A.
And I would like to introduce class B for Users with role B for read-only access. Preferably they shouldn't even be aware of Class A and P1 existence .
What is the easiest way to introduce it and manage it?
Use some proxy-classes? Property-level security?
Or to Introduce usual class B with only property P2 and manage the update operations together with Class A?
Comments
VIews work for SQL access. I really need a kind of view, but for classes paradigm: so I could have access to Class B property P2 with Caché object access.
Thanks, Vitaly!
That looks exactly like what I want. I'll give it a try. Except with not very clear magic with SQLStorage.
Is there any way to "generate" compatible storage? And why SQLStorage? Why not default CachéStorage, but copy-pasted from Class A?
It looks more like column-level security.
If you only need SQL access, then will be easier to create a view (CREATE VIEW), if need both, then - %CacheSQLStorage, e.g.:
<FONT COLOR="#000080">Class demo.A Extends %Persistent
</FONT><FONT COLOR="#000000">{
</FONT><FONT COLOR="#000080">Property </FONT><FONT COLOR="#000000">P1;
</FONT><FONT COLOR="#000080">Property </FONT><FONT COLOR="#000000">P2;
</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Fill()
{
</FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000000">..</FONT><FONT COLOR="#0000ff">%KillExtent</FONT><FONT COLOR="#000000">()
</FONT><FONT COLOR="#0000ff">f </FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">=1:1:3 </FONT><FONT COLOR="#800080">{
</FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">t</FONT><FONT COLOR="#000000">=..</FONT><FONT COLOR="#0000ff">%New</FONT><FONT COLOR="#000000">()
</FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">t</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">P1</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"P1_"</FONT><FONT COLOR="#000000"></FONT><FONT COLOR="#800000">i
</FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">t</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">P2</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"P2"</FONT><FONT COLOR="#000000">_</FONT><FONT COLOR="#800000">i
</FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#800000">t</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">%Save</FONT><FONT COLOR="#000000">()
</FONT><FONT COLOR="#800080">}
</FONT><FONT COLOR="#000000">}
</FONT><FONT COLOR="#000080">Storage </FONT><FONT COLOR="#000000">Default
{
<<FONT COLOR="#000080">Data </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"ADefaultData"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"1"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>%%CLASSNAME</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"2"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>P1</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"3"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>P2</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Data</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">DataLocation</FONT><FONT COLOR="#000000">>^demo.AD</</FONT><FONT COLOR="#000080">DataLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">DefaultData</FONT><FONT COLOR="#000000">>ADefaultData</</FONT><FONT COLOR="#000080">DefaultData</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">IdLocation</FONT><FONT COLOR="#000000">>^demo.AD</</FONT><FONT COLOR="#000080">IdLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">IndexLocation</FONT><FONT COLOR="#000000">>^demo.AI</</FONT><FONT COLOR="#000080">IndexLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">>^demo.AS</</FONT><FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>%Library.CacheStorage</</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>
</FONT>}
}</FONT>
<FONT COLOR="#000080">Class demo.B Extends %Persistent </FONT><FONT COLOR="#000000">[ </FONT><FONT COLOR="#000080">Final</FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#000080">StorageStrategy </FONT><FONT COLOR="#000000">= Default ]
{
</FONT><FONT COLOR="#000080">Parameter </FONT><FONT COLOR="#000000">READONLY = </FONT><FONT COLOR="#000080">1</FONT><FONT COLOR="#000000">;
</FONT><FONT COLOR="#000080">Property </FONT><FONT COLOR="#000000">P2;
</FONT><FONT COLOR="#000080">Storage </FONT><FONT COLOR="#000000">Default
{
<<FONT COLOR="#000080">SQLMap </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"BDefaultData"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Data </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"P2"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Piece</FONT><FONT COLOR="#000000">>3</</FONT><FONT COLOR="#000080">Piece</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Data</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Global</FONT><FONT COLOR="#000000">>^demo.AD</</FONT><FONT COLOR="#000080">Global</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">RowIdSpec </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"1"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Expression</FONT><FONT COLOR="#000000">>{L1}</</FONT><FONT COLOR="#000080">Expression</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Field</FONT><FONT COLOR="#000000">>ID</</FONT><FONT COLOR="#000080">Field</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">RowIdSpec</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Subscript </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"1"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Expression</FONT><FONT COLOR="#000000">>{ID}</</FONT><FONT COLOR="#000080">Expression</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Subscript</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>data</</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">SQLMap</FONT><FONT COLOR="#000000">></FONT>
<<FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">>^demo.AS</</FONT><FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">></FONT>
<<FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>%CacheSQLStorage</</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">></FONT>
}
}</FONT>
Result:USER><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">demo.A</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Fill</FONT><FONT COLOR="#000000">()</FONT>USER><FONT COLOR="#0000ff">d $SYSTEM</FONT><FONT COLOR="#008080">.SQL</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Shell</FONT><FONT COLOR="#000000">()</FONT> SQL Command Line Shell
The command prefix is currently set to: <
>. Enter q to quit, ? for help. USER>><FONT COLOR="#0000ff">select </FONT><FONT COLOR="#000080">* from </FONT><FONT COLOR="#008000">demo</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#008000">A</FONT>
select * from demo.AID P1 P2 1 P1_1 P2_1 2 P1_2 P2_2 3 P1_3 P2_3
3 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.1426s/46110/260143/45ms execute time(s)/globals/lines/disk: 0.0004s/16/809/0ms cached query class: %sqlcq.USER.cls12
USER>><FONT COLOR="#0000ff">select </FONT><FONT COLOR="#000080">* from </FONT><FONT COLOR="#008000">demo</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#008000">B</FONT> 2. select * from demo.B
ID P2 1 P2_1 2 P2_2 3 P2_3
3 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0696s/44550/243602/0ms execute time(s)/globals/lines/disk: 0.0002s/4/619/0ms cached query class: %sqlcq.USER.cls13
USER>>quit
USER><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">demo.B</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%OpenId</FONT><FONT COLOR="#000000">(3).</FONT><FONT COLOR="#0000ff">P2</FONT> P2_3
Is there any way to "generate" compatible storage? And why SQLStorage? Why not default CachéStorage, but copy-pasted from Class A?Unfortunately, to use %CacheStorage will not work, since at compile occurs the error:
ERROR #5564: Storage reference: '^demo.AD' used in 'demo.B.cls' is already registered for use by 'demo.A.cls' > ERROR #5030: An error occurred while compiling class 'demo.B'
That error can be removed by setting MANAGEDEXTENT class parameter to 0 as Extent Manager would stop tracking globals for that class.
Indeed, thank you:
<FONT COLOR="#000080">Class demo.B Extends %Persistent </FONT><FONT COLOR="#000000">[ </FONT><FONT COLOR="#000080">Final </FONT><FONT COLOR="#000000">]
{
</FONT><FONT COLOR="#000080">Parameter </FONT><FONT COLOR="#000000">MANAGEDEXTENT </FONT><FONT COLOR="#000080">As </FONT><FONT COLOR="#000000">INTEGER [ </FONT><FONT COLOR="#000080">Constraint </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#800080">"0,1"</FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#000080">Flags </FONT><FONT COLOR="#000000">= ENUM ] = </FONT><FONT COLOR="#000080">0</FONT><FONT COLOR="#000000">;
</FONT><FONT COLOR="#000080">Parameter </FONT><FONT COLOR="#000000">READONLY = </FONT><FONT COLOR="#000080">1</FONT><FONT COLOR="#000000">;
</FONT><FONT COLOR="#000080">Property </FONT><FONT COLOR="#000000">P2;
</FONT><FONT COLOR="#000080">Storage </FONT><FONT COLOR="#000000">Default
{
<<FONT COLOR="#000080">Data </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"BDefaultData"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value </FONT><FONT COLOR="#800000">name</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"3"</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>P2</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Value</FONT><FONT COLOR="#000000">>
</</FONT><FONT COLOR="#000080">Data</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">DataLocation</FONT><FONT COLOR="#000000">>^demo.AD</</FONT><FONT COLOR="#000080">DataLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">DefaultData</FONT><FONT COLOR="#000000">>BDefaultData</</FONT><FONT COLOR="#000080">DefaultData</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">IdLocation</FONT><FONT COLOR="#000000">>^demo.AD</</FONT><FONT COLOR="#000080">IdLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">IndexLocation</FONT><FONT COLOR="#000000">>^demo.AI</</FONT><FONT COLOR="#000080">IndexLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">>^demo.AS</</FONT><FONT COLOR="#000080">StreamLocation</FONT><FONT COLOR="#000000">>
<</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">>%Library.CacheStorage</</FONT><FONT COLOR="#000080">Type</FONT><FONT COLOR="#000000">></FONT>
}
}</FONT>
You are right Eduard. Column level security would be enough. It is even simpler!
Hi!
It looks like you are trying to implement security on your class model instead of just configuring it. I think you only need a single class with all the properties. Then you will give user A full access to the table by configuring this user on a Role that gives him INSERT, DELETE, UPDATE, SELECT privileges.
User B would be assigned to another role that would give it SELECT privilege only.
And if User B can only see a subset of columns from your table, then configure row level security using the Role information on $Role. InterSystems documentation here explains row level security configuration very clearly.