Well, it all works if I force feed the params list. If I do get it, I'll let you know. Thanks for all the help!
- Log in to post comments
Well, it all works if I force feed the params list. If I do get it, I'll let you know. Thanks for all the help!
No, it's just how I'm copying code into here... Here is what I am using:
^llctemp("query")="SELECT * FROM MyTable WHERE ((DateTime >= '2017-07-11 00:00:00') AND (DateTime <= '2017-07-18 23:59:59')) AND (Claim->Status->Name IN ('Paid')) AND (Claim->ClaimNumber = ANY (Select Claim->ClaimNumber from MyTable where Claim->ClaimNumber %INLIST ? SIZE ((10)) )) "
"returnParams")=1
^llctemp("returnParams",1)=$lb("2902700","2869840","2870820","")
That's my test data. Then I say:
Set status = tRS.Execute(filterParams...) // < -- look, I need the ... !!
Turns out I LITERALLY need the ... ( ! )
Now I'm not getting the <LIST> but I'm also not getting data! Sigh. I'll get it. I am rushing a little, so that's messing me up. I think with all this, I can get it to work at some point. Thanks!!
No, I am using %INLIST -- it's all the other where clauses that are using IN, with concatenation. I had read that %INLIST would use the same cached query, or something, so decided %INLIST was better, then ran into this problem where I now have to pass in an Execute parameter, whereas all of the other params are basically hard-coded.
Here's a copy of my query that was built:
SELECT * FROM MyTable
WHERE ((DateTime >= '2017-07-11 00:00:00') AND (DateTime <= '2017-07-18 23:59:59')) AND (Claim->Status->Name IN ('Paid'))
AND (Claim->ClaimNumber = ANY (Select Claim->ClaimNumber from MyTable where Claim->ClaimNumber %INLIST ? SIZE ((10)) ))
The last line is mine. Shoot. It was working when I passed in a $lb like this:
set filter = $listfromstring(filterClaims) //build my $lb() for the Execute parameter
set returnParams = $lb(filter) //$lb($lb(1,2,3), hypothetical param2, ... ,paramN)
set param1 = $lg(returnParams,1) //param1 = $lb(1,2,3)
// ... set paramN = $lg(filterParams,n) ...
Set status = tRS.Execute(param1)
but now, with the returnParams as an arity (that's cool, btw), I'm getting a <LIST>. If necessary, I'll go back to the forced "param1" thing, because this is an urgent fix. I can play around with it later.
Yes, exactly. We are building the sql statement line by line; the where clauses are built based on user input, and the whole mess is concatenated with actual values. hmm. But essentially, it looks like the params array is what I needed. Except that I'm getting a <LIST> error, so I have to go back and look at that. I'm on 2014.1.3.
Thanks!
Oh no, good to know about concatening values instead of using ? Execute params. I should go rewrite the entire query! It has so many optional parameters though, so I'll go look at the Rubens/Timothy solution. Thanks.
So this is not a good way to build the where clause:
"Set where(16)=InstancePrefix_"PayerName IN ("_valuelist_")""
?
I think it's a bit safer anyway to know exactly how many parameters to expect (avoid a sql injection attack?); I can see how long the one statement with a known number of parameters takes compared to other statements. So far I have just the one unknown parameter, and all the rest are built into the sql. Thanks.
Oh -- the parameter is a $lb(); the sql is using %INLIST, which, in the example in the documentation, seems to require a parameter passed in the %Execute() rather than built into a value in the sql statement. The user might or might not search on this field. It's more like:
select * from person where ID %INLIST ?
I was thinking that since I need to pass this in to the Execute, I might as well set up a structure in case we add more parameters to the statement in the future; but perhaps it's better to just pass in a known number.
Thanks.
Yes, I needed the SqlComputeOnChange = property; now it's getting updated for every insert/update when the {property} changes, which is great. The only problem is, my object is not limited to one row. It's an object with dynamic properties, where one object is several rows, and one row is the Category and Value of one property.
This is fine when I insert or update ALL the rows for one object, but not when I update just one row. I can't think of a good way to force update on %READERLIST for other rows, based on a WHERE clause, maybe.
ROWLEVELSECURITY = 1;
///Updated for each row when the property 'Value' changes
Property %READERLIST As %String [ SqlComputeCode = {set {*} = ##class(Users).%SecurityPolicy()}, SqlComputed, SqlComputeOnChange = Value ];
Here's an example of my data:
ID %READERLIST Category Value Username
1 DEV Color Blue Laura
2 DEV Number 555 Laura
3 QA Color Pink Jane
4 QA Number 87 Jane
I'll be mulling this over for a while, but would love any other ideas.
Thanks,
Laura
I can say first hand that John is very patient and thorough. Good move!
Huh. I have been unable to have my class update %READERLIST and the %RLI index when I update a row. It's set when I create/save an object, but it's not updated when I update the object. Can I show you some simple test classes?
My %READERLIST is based on a role name.
From OVERRIDEing the Parameter ROWLEVELSECURITY:
"
/// ROWLEVELSECURITY = 1 | <property> means that row level security is active and the list
/// of users/roles for a given instance/row is contained in a generated property. If
/// the value of this parameter is a valid property name then that property will be
/// used as the reader list and only generated if not already defined.
ROWLEVELSECURITY = 1;
"
?
Is is possible to change the %RLI index when a row is updated? Or, change the %READERLIST value for the row?
Yes, thanks all. They all worked on 2014 as well.
That's it! Thank you! You might have been thinking "it's so obvious", but I wasn't thinking of adding the attributes via html, just for that cell. I was only thinking of "write", or using the column object to set its cellTitle property, which is not feasible since I can't get the column obejct. But substituting html works great. Thanks!
I was able to change the style just for cells whose columns are greater than pSeed (nice touch there), and I removed the showValueInToolTip for the rest of the cells. I'm sure that would have been noticed at some point.
Laura
And by the way I like the <pre> tag - nice touch. Can we call ..EscapeHTML or does that require a license/connection?
I could try that... but this is for the user-friendly page, and I don't want that info on it. I was hoping to log the info in the error trap.
I just tested the error trap, and found that you can, actually, load the details of an object, while still in the error traap ^%ER. I had no idea:
Error: 7
7. <UNDEFINED> --routine name -- *undef : CSP Error at 9:41 am. $I=|TCP|1972|16772 ($X=327 $Y=14056) $J=16772 $ZA=24576 $ZB=$c(0) $ZS=262144 ($S=267019816)
set x=undef //LLC testing
Variable: %request (copying data ... done)
(base stack level = 20)
%request = <OBJECT REFERENCE>[1@%CSP.Request]
Show object details? yes
+----------------- general information ---------------
| oref value: 1
| class name: %CSP.Request
| reference count: 6
... etc
So, as long as the error trap has that info available, I'm pretty good. Does that change in 2016? Why is the %CSP.ErrorLog page in 2016 so careful to save off the request, session, and response objects inthe error log?
Thanks,
Laura
Can this be used in the System/Security Management/System-side Security Paramters "password pattern" at all? I'm trying to find a way to match a user's password to something much more restrictive than "8 to 32 alpha, upper, lower, numeric, OR punctuation". I'd like to require at least one of a few kinds of characters (1 upper, 1 punctuation).
I have a nice regex for this, but I'm not sure if I can use a regex, or this cool datatype, in the above mentioned System setting. Any recommendations?
John, thanks for the response. I might ask this explicitly in the DC later, but I'll have to look at my notes before I do, as I'm sure I'll get some flak. Just for informational purposes: we were instructed to restrict our users to a single tab in our web application. I know this is not how a web application is supposed to work (according to many users on google groups writing about this sort of thing), but the directive came from above. I wrote simple logic to check if a user has a session already, and if so, he can't open a second tab. Sometimes, due to a bug, or timeouts on queries, the logic thinks the user has a second session when he does not. At that point the user can't even log into the application. It's at this point that he'll have to call IT, who will have to call a developer, who can then kill his sessions manually from the management portal. I was trying to give the users a way to "reset" or "clear" their own sessions (logging in first to a separate page perhaps) so that they can use the application again. When I first began to look at this, I realized that most of this has to be done in the %SYS namespace; hence the problem, and its relavance to this question about changing their own password.
However, it hasn't been an issue (yet), so it's on my back burner. When it becomes an issue I'm sure I'll be back asing about it.
As always, thanks for the help.
Laura
Since we're on the subject, do you have a magic way of letting users kill their own sessions (in case the application is not letting them log in) in the CSP Sessions global? I'd love to give them a "Log out of all sessions" button.
Thanks,
Laura
Oh, hello John! I was not aware of it, AND it seems to work for an average user without requiring access to the %SYS namespace. That is great! Thanks so much.
That was easy!
Laura
Well, that helps. Thanks. I can't forsee any reason to make it more complicated, unless there is some huge benefit that I'm overlooking? Ah, I was thinking of changing the emails to a list of users, and getting their emails from the user table, so maybe that's a future benefit.
I extended the %Net.MailMessage for one object, and had such a pain of a time stuffing the To prooperty and retrieving it again (it's a list of %String). This time I was thinking a simple %String would be nice, as long as I can use it with SQL, and stuff.
Thanks,
Laura
Hi All, Thanks for the help. I learned a lot, and looked at our actual instances with a more critical eye. I'll be recommending some changes on them. I had installed the unlicensed version of 2016 on my computer just for fun, so there were no license issues (but that's something to keep in mind for our other instances). I had installed 2016 with minimal security, then tried to increase the security to match our development instance. That was actually successful; it was the disabling of all the other users that messed it up.
Thanks,
Laura
Ah, thank you. While playing around with ^SECURITY, I had enabled all users EXCEPT UnknownUser (of course), and after checking out the auditing area of ^SECURITY (thanks for that tip), I saw that it was indeed UnknownUser that is trying to log into the management portal.
I did not know about that step, that the CSP gateway has to log in first to serve up a page. What user is normally used for this?
Thanks,
Laura
So, I created a new Role, called TaskAdmin. To this role, I added 3 privs:
%Admin_Task
%DB_CACHESYS
%Development (so user could login to terminal)
I included the only tables I could find in %SYS that are relevant:
%SYS_Task.Config
%SYS_Task.History
I added my own tables and views that use these tables.
To this role, I added a new user called task_admin, to test a query from the terminal. i.e. I created a new user, task_admin, and added this single role.
This test user, task_admin, can run the SQL shell in the terminal and select * from %SYS_Task.History. (%SYS.Task is a different story!). So, success.
In order for a real user to see my table, which is a mix of %SYS.Task, %SYS_Task.Histoy, and my own tables, and my own view based on those same tables, I had to add my own tables and views to the role TaskAdmin as well; but it did work finally work.
I also remembered seeing somewhere that embedded SQL does not check SQL privs, which is maybe why I could see all this table data in the <tablePane> without adding tables. (?) I admit that the user looking at my table data did have %Admin_Task and %DB_CACHESYS privs already (from some role). But the user could not call a query/view that queried %SYS_Task.History directly until I added my own tables.
Yes, I really l mean that about the <tablePane>. My user had the privs, but not the tables, and yet could see the data in a <tablePane>. Is this included in embedded SQL?
So, basically I think I got it, and the real user needed my tables/view added to a role; the test user needed the %SYS tables added since he (it) was querying just the %SYS tables.
Thank you for your help!
Laura
OK, thank you; We'll have to take a look.
I need some kind of Authenticated flag to check, and if false, use OnPreHTTP to call %response.Redirect...
But the login page is already the Login page, which forces the user to authenticate first; it also go to the login page upon logout.
I tried this on some random web application; clicking on a bookmark brought me immediately to their login page; I didn't even have to login to get redirected.
Ooh, fun, thanks. Can I use it with %ListOfDataTypes too? I have both returned from methods, and I need to use them in the %session array.
But what else? How do you run it? Do you need to be in a %csp session?
I have the %ALL:
%SYS>w $roles
%All
Attempt to run it as a class method:
%SYS>w ##class(%CSP.Session).LogoutAll("laura1")
W ##CLASS(%CSP.Session).LogoutAll("laura1")
^
<METHOD DOES NOT EXIST> *LogoutAll,%CSP.Session
%SYS>
Try it with a session object:
%SYS>s session=##CLASS(%CSP.Session).%OpenId("fBOZJihk0C")
%SYS>w session.LogoutAll("laura1")
quit $$LogoutAllUserSessions^%SYS.cspServer(username, %request, %response ) }
^
<UNDEFINED>zLogoutAll+1^%CSP.Session.1 *%request
And from a connected session with the user that needs to logout of all sessions:
ERROR #822: Access Denied
%SYS>
So I'll have to connect to the application as a developer in order to kill off sessions that are causing problems. The user can't wipe them out himself. I get a "problem session" if the page times out and the user kills the page; this causes the session to hang around until its timeout, and due to our specific setup, he can't log in again until the session times out or a developer kills it from Session Management. I wanted to give the user the ability to wipe out all of his own sessions.
No need to discuss grouping by sessionId or anything like that - we have a very specific setup such that flags are set and the user can't login again if he kills his page.
Now, how can I intercept this error and call the logout? That would be better...

Thanks,
Laura
That's a good idea. For future reference, here's an example:
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…
(see the Example section). I'll probably try it after consulting with some other programmers. I'm not sure it's critical to know, at this point. But it would be interesting to know.
Thanks,
Laura
AH. IF we use cookies, they will be stored in the Session Cookie Path. We don't, but the Application must use at least the one login cookie to pass authentication between applications. I can see it in my browser, and it's called CSPSESSIONID +other stuff.
I'm thinking that this login cookie would be used somehow if the Login Cookie is selected? Or not used? We don't want that either-- we like the continuity between applications. But, what does happen if the Login Cookie is selected in the web application?
What could we store in a cookie? Can we possibly find out if a second tab has been opened by using a cookie?
nope. i'm talking about Cache web applications. There are two properties onthe web application page: Loging Cookie, and Session Cookie Path. The Session cookie path is a path. We set all of our applications to use the same session cookie path. We do not have the Login Cookie option set. So, presumably all of our applications use the same (cache) authentication because of the session cookie path.
Jsut wondering what would happed if I check that little Login COokie Path box.
I also see cookies in my browser, with the same session ID as some of my CSP sessions. Wondering what that cookie is. Not a login cookie?
Thanks,
Laura