SoftInstigate / restheart

Rapid API Development with MongoDB
https://restheart.org
GNU Affero General Public License v3.0
807 stars 171 forks source link

An issue with accessing resource for unauthenticated user #100

Closed mohankv closed 8 years ago

mohankv commented 8 years ago

$unauthenticated user is not able to access resources under path="/io/tracks/". It always returns back 401 Unauthorized error. But if I configure it like path="/", it works without any issues. What is wrong with following code?

- role: $unauthenticated predicate: path-prefix[path="/io/tracks/"] and method[value="GET"]

HTTP Error: `401 Unauthorized

Connection: keep-alive Access-Control-Allow-Origin: * WWW-Authenticate: Basic realm="RestHeart Realm" X-Powered-By: restheart.org Access-Control-Allow-Credentials: true access-control-expose-headers: Location, ETag, Auth-Token, Auth-Token-Valid-Until, Auth-Token-Location, X-Powered-By Content-Length: 0 Date: Fri, 29 Jan 2016 03:55:13 GMT`

mkjsix commented 8 years ago

First check the indentation. It's a YAML file, so indentation matters. In case you write everything in a single line I'm not sure the interpreter can handle your configuration correctly.

permissions:
 - role: $unauthenticated
   predicate: path-prefix[path="/io/tracks/"] and method[value="GET"]

See: https://softinstigate.atlassian.net/wiki/x/IgDM

mkjsix commented 8 years ago

Could you please post here your complete security configuration file? (Please remove any sensible data like passwords first)

mohankv commented 8 years ago

Hello Maurizio, Thanks for helping me on this. Here is the complete security configuration.

With this configuration, $unauthenticated user is not able to access resources under "/io/tracks/" and "io/accounts"

If I update the paths like predicate: path-prefix[path="/"] and method[value="GET"] , $unauthenticated user is able to access those resources.

mkjsix commented 8 years ago

Hi @mohankv

Have you already enabled the database authentication? With your security file RESTHeart even refuses to startup, as it is missing the mandatory 'users' section.

However, I added by hand an admin on my local copy of your file:

users:
    - userid: a
      password: a
      roles: [admin]

Then admin created a couple of resources (with httpie, but curl works as well):

$ http -j -a a:a PUT localhost:8080/io
$ http -j -a a:a PUT localhost:8080/io/tracks
$ http -j -a a:a PUT localhost:8080/io/accounts

Then I tested the first resource with $unauthenticated:

$ http GET localhost:8080/io/
HTTP/1.1 401 Unauthorized

This is OK, as $unauthenticated can't GET the /io path. Then I GET the /io/tracks and it worked:

$ http GET localhost:8080/io/tracks
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ETag, Auth-Token, Auth-Token-Valid-Until, Auth-Token-Location, X-Powered-By
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 120
Content-Type: application/hal+json
Date: Sat, 30 Jan 2016 15:58:26 GMT
ETag: 56acda51647d678037540ad4
X-Powered-By: restheart.org

{
    "_collection-props-cached": false, 
    "_etag": {
        "$oid": "56acda51647d678037540ad4"
    }, 
    "_id": "tracks", 
    "_returned": 0
}

I did the same for /io/accounts successfully, so the $unauthenticated user can PUT documents under the /io/accounts path as specified.

I suspect your problem might be that you roles names in sections users or dbim don't match with the role names in the permissions section below. Please double check this.

mohankv commented 8 years ago

Hello, Thanks for your reply again.

Having SimpleFileIdentityManager works for me too. But I would like to use DbIdentityManager. And the request has to be unauthenticated. In your requests you are passing creds.

Thanks

mkjsix commented 8 years ago

No, I'm not. I'm passing credentials only when creating the resources, as the unauthenticated can't do that as per your configuration file (it hasn't permissions to create the necessary databases and collections). As you can see all the remaining calls via httpie are unauthenticated. You shouldn't experience something different when using a DbIdentityManager, please check the roles in your users collection.

mkjsix commented 8 years ago

Hi @mohankv I can confirm here it works as expected also using the DbIdentityManager: unauthenticated requests can GET and PUT the configured resources (respectively /io/tracks/ and /io/accounts).

ujibang commented 8 years ago

Hi @mohankv , @mkjsix

I made some test with DbIdentityManager and $unauthenticated role. I confirm that it works as expected.

@mohankv please double check that you are not passing credentials in the requests supposed to be $unathenticated (otherwise that permission won't apply).

Take into account that browsers cache basic authentication credentials, for instance if you have previously accessed the /browser section.

To clear browser auth cache, try http://wronged:wrongpwd@127.0.0.1:8080/browser, the auth popup windows should show and click cancel.

mohankv commented 8 years ago

I am using only curl or http commands to test... so browser cache should not be the issue. I am using restheart-1.1.4. I am attaching both ymls for your reference; can you please take a look into this. I really appreciate your help. Thanks in advance.

mkjsix commented 8 years ago

Honestly I can't see anything wrong in your configurations so far. Also the roles in the db seems to be mapped correctly to permissions.

@ujibang the only visible difference I can spot here is auth-token-enabled: false while usually our tests run with the auth token.

@mohankv could you please run a quick test with auth-token-enabled: true and check for any difference?

ujibang commented 8 years ago

Hi @mohankv

I replicated the issue. The problem seems to be in undertow code: the bug is in method the method isAuthenticationRequired(). of SimpleAccessManager class.

As you can see, if the request resolves against the predicates specified for the $unautenticated role, it should return true thus not requiring authentication for the request.

Using the debugger I can see it iterating over the two predicates specified in your security conf file, however it does not resolve for both.

@Override
    public boolean isAuthenticationRequired(final HttpServerExchange exchange) {
        if (getAcl() == null) {
            return true;
        }

        Set<Predicate> ps = getAcl().get("$unauthenticated");
        return ps == null ? true : !ps.stream().anyMatch(p -> p.resolve(exchange));
    }

I'll provide an update as soon as possible...

ujibang commented 8 years ago

Hi @mohankv

I found the problem! It is not a bug in undertow, just the predicates are resolved against the relative path of the request (i.e. not taking into account the prefix path used to "mount" the request handlers).

The path specified in predicates must be expressed taking the resource URL mapping into account (set by mongo-mounts configuration option).

The rule is

When the URI of a resource is remapped via mongo-mounts configuration option, the path attribute of permissions must be relative to the what argument of the mongo-mounts option.

I updated the documentation as well, see https://softinstigate.atlassian.net/wiki/x/IgDM#EnableandConfigureSecurity-SpecifyPermissiononURIRemappedResources

For example, given the following mongo-mounts configuration, the collections of the db GFDB are actually bound to the URI _/io/[collname]

mongo-mounts:
    - what: /GFDB
      where: /io

To allow GET requests on /GFDB/tracks collection:

permissions:
 - role: $unauthenticated
   predicate: path-prefix[path="/tracks"] and method[value="GET"]

Let us know if this fixes your problem.

mohankv commented 8 years ago

Thank you so much! That fixes the issue I have.

Works perfect. I really appreciate your time.