Rubens Silva · Sep 11, 2017 go to post

Hello Maks.
Not that I'm against using e-mail, however I think it's more resourceful if we keep the discussion opened for whoever has interest on participating. We could use the community forum as a start point and as the project expands into something solid we migrate to a github issue.

Rubens Silva · Sep 15, 2017 go to post

Hello, sorry for the bump again.
I have updated the original post to include a tutorial. This is only the Part 1!

Rubens Silva · Sep 18, 2017 go to post

Added demo explaining stream usage. This ends the Part 1.
P.S.: No tag for tutorials?

Rubens Silva · Sep 19, 2017 go to post

why didn't someone else think of this before?

As far as I know, VueJS uses property accessors to ovewrite set/get behavior. When AngularJS came out there wasn't an estabilished way of reproducing that, so who takes the blame here was the slower JavaScript development, untill ECMAScript 5 came out. Mostly because IE of course.

Rubens Silva · Sep 21, 2017 go to post

Hello Evgeny! Sure!
 No. 5 means that instead of showing the editor that creates a reply, leave it hidden untill the user clicks Add new comment. This way this editor won't appear before the one that creates an answer. 
Doing that, it's possible to prevent replies that should be an answer, so that they can be checked by the OP.

No 7. is simple, but I'll elaborate it to explain the why. I have noticed that the community contributes a lot with tutorials (including mine), but there's no way to classify them in that way, leaving them mixed with unrelated threads. Several times, these tutorials get lost because they disappear from the user's visibility, since they get pushed down by newer threads.
So if you introduce a tag or even a group to correlate them, it would make easier to search them.
 

Rubens Silva · Sep 21, 2017 go to post

Nice, but to prevent flooding the OP's e-mail inbox you could introduce an internal timer that gets triggered when a new answer is posted.
e.g., if I post a new thread asking for help with something. And someone posts an anwer, as I'm the OP I'll be notified by e-mail if I don't see the thread again within 15 minutes. If I still don't see the thread, wait another day and send the e-mail again.

Well, something like that.
Actually... forget my idea, I guess it would complicate and annoy OPs even more needlessly.

Rubens Silva · Sep 22, 2017 go to post

This is one of the reasons that I asked to bring some highlight to tutorials, since most of tutorials are related to community efforts or newer ISC technologies.

Rubens Silva · Sep 22, 2017 go to post

I think the community as a whole take some blame as well. Since Caché is robust enough to make us think  that we can only use features provided and audited by ISC themselves., you can see that when comparing the number of questions with articles/anouncements. 
So far, my impressions are that most of Caché developers prefer to take the safest route instead of trying what the community provides. There're exceptional projects as well, like WebTerminal but such projects still represent a minor part of us using it.
This subsequently results in open-source projects being abandoned due to the lack of interest as user and developer (maintainers). My proof when saying that is the amount of replies and attention a thread that presents a new project gets.
Also, if you don't want to lose time trying to convince your boss about open-sourcing a tool, do it outside and bring it to where you work. This way you'll be able to say that the project is your initiative and not something on behalf of the company you work. Just remember to design it in a way that won't use any business code.

Rubens Silva · Sep 22, 2017 go to post

You're referring that what you see as an OP, which is not the one who suffers with this problem.

Hide the editor that is X marked. Untill the user clicks Add new comment.
This way the circulated one is shown before.
And I just noticed that this gets worse when a question has many replies as each reply pushes the answer editor further below.

Rubens Silva · Sep 25, 2017 go to post

Hello Soufiane, although your thank you is appreciated, an acceptance check is even more, because it alerts users with the same issue as you that a solution has been provided already.
https://community.intersystems.com/post/convert-timestamp-its-correspon…
You'll also help us if you use the same thread whenever you think about making a similar post. Don't create new threads if your subject is the same as the one you already created, or the community will start downvoting you.
Remember that downvoted threads have less chance of getting attention.

Rubens Silva · Sep 25, 2017 go to post

Nope! Click on the check right before the "ANSWER" and you'll mark this answer as accepted. Remember that only you, the OP, can do it!

Accepting answers will move the answers counter up!

This way you don't even need to reply with a thanks, as we consider checking an answer and upvoting a post the same as that.

Rubens Silva · Oct 2, 2017 go to post

Hello Daniel, I'm trying to figure how to make a REST resource available through JWT access token.
I noticed that you used:
set accessToken=##class(%SYS.OAuth2.AccessToken).GetAccessTokenFromRequest(.sc)


For your resource oauth2test.demoResource, but it doesn't seem like this method validates the access token itself.
As it only checks for http header validity.

Does this means I'm obligated to call the introspection method or should I simply use:
##class(%SYS.OAuth2.Validation).ValidateJWT(..#OAUTH2APPNAME,accessToken,"scope1 scope2",,.jsonObject,.securityParameters,.sc)
Any help regarding this doubt is appreciated.

Rubens Silva · Oct 31, 2017 go to post

Implementing IntelliSense seems to require a major overhaul of the current extension as it requires the usage of a language server and a language client that sends and retrieves a lot of essential metadata for code diagnostics. Some of that I don't know if InterSystems publicly discloses.
A brief protocol explanation: https://code.visualstudio.com/blogs/2016/06/27/common-language-protocol
The full protocol: https://github.com/Microsoft/language-server-protocol/blob/master/proto…

The implementation using NodeJS: https://github.com/Microsoft/vscode-languageserver-node
On the last repository you will notice that it actually contains the JSONRPC 2.0 message definition, the protocol, and finally the base server and client implementations.

Rubens Silva · Oct 31, 2017 go to post

Does accessing the login page creates a session and uses a license? Or only after authenticated?

Rubens Silva · Nov 3, 2017 go to post

No, you need to grant the group/user you're using for Caché permission to access the executable that's being denied.
On Windows, Caché uses the same user that started the process. Remember that you're trying to access a resource natively from your OS, so your Caché user is NOT what you should check.

If you need to know which you should be looking for, open a cmd and type "whoami".
Now you'll need to have an account with Administrator privileges, otherwise you won't be able to edit your executable's permissions. If you're working in company network, then your user probably wont have enough privileges for changing that file's permissions, so I'd recommend leaving this task for your IT technician.

The technician should know what to do,  just ask him to add your user to list of groups/users that are allowed to execute your file. Normally you would only need "read and execute", unless that executable also generates some file somewhere, so also grant your user the permission to write.

Rubens Silva · Nov 7, 2017 go to post

It actually works and it can even transport objects using %session.Data, it also seems to run in a separate process from the request, which means a non-blocking operation. But I could  only make it work by defining the event class on the web application config even though there's a way to define it dynamically by using %session.EventClass.
So my only question is: Is there a moment for setting this up? Like when using OnPreHTTP for %response? I tried setting it using the classmethod Page after calling the superclass (%CSP.REST) but no avail. And %CSP.REST doesn't provide OnPreHTTP because it overwrites the Page method completely.
Now regarding the data we can retrieve from the default %CSP.REST implementation is none.  As we only have access to %session and %request (I didn't tried %response), it lacks any metadata that is used for %CSP.REST based applications (dispatch method, dispatch class, route arguments, stack, etc) since no object is created for it.

That means you still need a way to retrieve this info, this is why I had to transport the object over by using %session.Data. And here's how I did it:
ClassMethod Page(skipheader As %Boolean = 1) As %Status [ ProcedureBlock = 0 ]
{
  new %frontier
  set %frontier = ##class(Frontier.Context).%New(%session, %request, %response)
  set %session.Data("%frontier") = %frontier
  $$$QuitOnError(##super(skipheader))
  return $$$OK
}
And the event class:
/// Called when we have finished processing this request
ClassMethod OnEndRequest() As %Status
{
  set frontier = %session.Data("%frontier")
  return frontier.ReporterManager.Report()
}
Now using %CSP.REST I think the best approach is to create an utility method that is called on the beginning of each dispatch method. This utility would populate %session.Data with context sensitive info like arguments received. This method must be called for each method that is dispatchable. An example:
ClassMethod PopulateLogObject(arguments As %List, method, classname)
{
    set log = ##class(MyApp.Log).%New()
    set log.username = $username // <--  This is the only one you can fetch directly when using %CSP.SessionEvents.
    set log.parameters = arguments
    set log.method = method
    set log.classname = $classname()
    return log.%Save()
}
ClassMethod DoSomethingRequestedOverHTTP(argA, argB, argC)
{
     try {
        $$$ThrowOnError(..PopulateLogObject($lb(argA, argB, argC), "DoSomethingRequestedOverHTTP", $classname())

     } catch e {
        // render error response.
     }
    // render success response.
}
I don't know of any other possible way from getting this kind of info, unless if you overwrite DispatchRequest from %CSP.REST.

Rubens Silva · Jun 19, 2019 go to post

That's the issue. You can't read any new environment variables after the Caché/Ensemble process (by that I meant at OS level, using a shell script) has been started. Not even Docker allows you to do that, since you must rebuild the container to "refresh" the variables. Otherwise that would be a security flaw as @Stefano Cairoli pointed out.

If you own the code, you can try simulating env variables by using globals, which is exactly the same thing the class Sample.Installer does or populate it from an external file, like a .env. Also $zf won't work because it always summons a new shell to execute your call, so even though the variables you set would be available there, it won't be there for your current process (again, OS level).

Rubens Silva · Aug 1, 2019 go to post

@Eduard Lebedyuk no, this is a misconception. In a nutshell CORS is the opposite, by default every green browser will apply the Same Origin Policy to allow or negate the access. However CORS has been made for making that policy lax (since it allow you to access a resource outside the origin's domain). 
Needless to say, this wouldn't prevent the attacker from GET'ing a resource because GET methods don't execute a pre-flight request, and that's just the tip of the iceberg, you can read more about it here:
About the misconception https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/september/common-csrf-prevention-misconceptions/
About the correct implementation https://www.acunetix.com/websitesecurity/csrf-attacks/

Btw, this implementation doesn't validate the token, neither it generates something randomically strong (using CPRNG). Checking if the header is present  is not enough. OWASP advises you to generate tokens that you can validate. It also suggests that the implementation should use stateful URLs, like the ones containing the token themselves (which is similar to the CSPToken that each private CSP page has).

Rubens Silva · Aug 21, 2019 go to post

UPDATE: Oh, this technique has been mentioned already, either way this sample shows why to use it.

That's gonna be quite the bump, but it's worth mentioning. Since the adoption of %Dynamic classes, you can now provide a JSON object to methods, this way you get a syntax that ressembles POJOs (Plain Old JavaScript Objects):

ClassMethod GreetAndAsk(data As %DynamicObject) As %String
{
    return $$$FormatText("Hello %1! %2", data.name, data.message)
}

// Omitted for brevity.
write ..GreatAndAsk({
  "name": "Rubens",
  "message": "How are you?"
})

This is really useful if you must provide a method with several parameters that have some sort of link between them, here's a real use-case:

/// This method implements a full-sized configuration on how to handle multipart uploads.
ClassMethod TestPOSTMultipartFileUpload() As %Status
{
  set location = %frontier.Data.Workspace_"/fixtures/uploads/multiple"
  set destination = (location_"/:KEY/:FILE_NAME:EXTENSION")

  // 512 KB
  set maxFileSize = (1024**2/0.5)

  return %frontier.Files.Upload({
    "hooks": {
      "onItemUploadSuccess": "Frontier.UnitTest.Router:WriteResultToProcessGlobal",
      "onItemUploadError": "Frontier.UnitTest.Router:WriteErrorToProcessGlobal",
      "onComplete": "Frontier.UnitTest.Router:WriteResultSummaryToProcessGlobal"
    },
    "filters": {
      "verbose": true,
      "maxFileSize": { "value": (maxFileSize), "errorTemplate": "The file :FILE_NAME exceeded the max of :VALUE bytes." },
      "extensions": ["cls", "md", "txt", "pdf"]
    },
    "destinations": {
      "file_a": { "path": (destination) },
      "file_b": { "path": (destination) },
      "file_c": { "path": (destination), "optional": true },
      "file_d": { "path": (destination), "optional": true },
      "files": {
        "path": (location_"/files/:INDEX/:FILE_NAME:EXTENSION"),
        "slots": 3,
        "filters": { "maxFileSize": 500000000 }
      }
    }
  })
}
Rubens Silva · Aug 22, 2019 go to post

Which have been broken already:

https://shattered.io/

Also. NIST considers SHA-1 as obsolete since 2011. SHA-256 is somewhat safer for now.

We have some customer projects that uses a separate user table, in these cases we usually use a stronger hash implementation:

Method SetPassword(value As %String) As %Status
{
  set i%password = ..HashPassword(value)
  return $$$OK
}

ClassMethod HashPassword(
	value As %String,
	salt As %String = "") As %String
{
  if salt = "" {
    set salt = $$$lcase(##class(%xsd.hexBinary).LogicalToXSD($System.Encryption.GenCryptRand(32)))
  }
  
  set hash = $$$lcase(##class(%xsd.hexBinary).LogicalToXSD($System.Encryption.PBKDF2(value, 15000, salt,256,256)))
  return $$$FormatText(hash_":"_salt)
}

Method IsPasswordMatch(plainTextPassword As %String) As %Boolean
{
  set salt = $piece(..password, ":", 2)
  return $$ConstantTimeCompare(..HashPassword(plainTextPassword, salt), ..password)
  
ConstantTimeCompare(a, b)
  if $length(a) '= $length(b) return 0
  for i=1:1:$length(a) {
    // Convert char to ASCII code and then to bitstring.
    set aChar = $factor($ascii($extract(a, i)))
    set bChar = $factor($ascii($extract(b, i)))
    set match = $bitlogic(aChar ^ bChar)
    set all = $bitlogic(all | match)
  }
  // 00000000 = valid
  return $bitfind(all, 1) = 0
}