Authorization

The Crane Server provides configuration properties for defining and customizing the authorization of each repository and/or sub-paths of repositories. There are two kinds of authorization provided by Crane “Path access” and “POSIX access”. Path access control is in the application.yml file of the app and provides a few options to define which users should have access to the files in a specific directory (repository or sub-path). POSIX access control provides an additional access control layer using the POSIX (Portable Operating System Interface) permissions of the file system. Additionally as Crane allows both reading and writing of files it has decoupled the access control of these actions.

Path access

Configuring the access control for read and write requests work in the same way. Crane’s default access control disallows all access. Concretely this means that granting access control to any user or group of users needs to be done explicitly. Crane server admins can define the path access control of each repository and path in the application.yml file. Crane uses this definition to determine whether users have authorisation to do the requested action for a given path.

For example, to configure the path access of the example_repository repository:

app:
  repositories:
    example_repository:
      read-access:
        # read access configuration
      write-access:
        # write access configuration

In addition to configuring the access control for the whole repository, it’s possible to define access control for paths inside a repository. This is completely optional, and not all paths that exist in the repository must be defined in the configuration. Crane checks the access control using the most specific configured path. For example:

app:
  repositories:
    example_repository:
      read-access:
      # read access configuration
      write-access:
      # write access configuration
      paths:
        my_path:
          read-access:
          # read access configuration for my_path
          write-access:
          # write access configuration for my_path
          paths:
            some_path:
              read-access:
              # read access configuration for some_path
              write-access:
              # write access configuration for some_path
            another_path:
              read-access:
              # read access configuration for another_path
              write-access:
              # write access configuration for another_path

Crane determines if a user has access to a path in recursive manner. It starts from the root, (i.e. a repository) and follows the path all the way down to the requested directory or file. For example, if a user requests /example_repository/my_path/some_path/file.txt, Crane first checks the authorization on example_repository, then on my_path and finally on some_path. If a user requests /example_repository/my_path/abc/file.txt, Crane first checks the authorization on example_repository, and finally on my_path (no check is done for abc, since is not specified in the configuration file).

Read access

The read access of a repository or path is specified using the read-access property.

By default (if the property is not specified), nobody has read access. Users making read requests to paths can get the following errors:

  • 404 not found:
    • when authenticated users visit an unauthorized repository or path
    • when authenticated users visit an authorized, but non-existing path
    • when unauthenticated users visit a non-existing path in a public repository or path
  • 302 Found:
    • when (using a browser) unauthenticated users visit an unauthorized path (no matter whether it exists)
  • 401 unauthorized:
    • when (not using a browser) unauthenticated users visit an unauthorized path (no matter whether it exists)

Write access

The write access of a repository or path is specified using the write-access property. Write access is completely independent of read access, therefore a user could have write access to a repository or path without having read access to it.

By default (if the property is not specified), nobody has write permission. Users making write requests to paths can get the following errors:

  • 403 forbidden:
    • when (un)authenticated users write to an unauthorized or non-existing repository or path

Reference

Crane provides the following properties to define read and write access:

Public

If set to true a repository is accessible by any authenticated or unauthenticated user.

Note: sub-paths can only be public if their parents are also public.

app:
  repositories:
    repository:
      read-access:
        public: true
    repository_with_paths:
      read-access:
        public: true
      paths:
        path1:
          read-access:
            public: true
        path2:
          read-access:
            public: false # equivalent to default behavior, can be removed

In this example the two repositories (i.e. repository and repository_with_paths) are public. In the repository_with_paths repository, path1 is public, however path2 isn’t accessible by any users.

app:
  repositories:
    repository:
      write-access:
        public: true
    repository_with_paths:
      write-access:
        public: true
      paths:
        path1:
          write-access:
            public: true
        path2:
          write-access:
            public: false # equivalent default behavior, can be removed

In this example the two repositories (i.e. repository and repository_with_paths) have public write access (i.e. any authenticated or unauthenticated user can upload files). In the repository_with_paths repository, path1 is public, however nobody has write access to path2 (since no other access control is specified).

Users

The users property grants access to users by listing their usernames.

app:
  repositories:
    repository:
      read-access:
        users: [ jeff, jack ]
    repository_with_paths:
      read-access:
        users: [ jeff, jack ]
      paths:
        path1:
          read-access:
            users: [ jeff ]
        path2:
        path2_explicit:
          read-access:
            public: false

In this example the two repositories (i.e. repository and repository_with_paths) are accessible by jeff and jack. In the repository_with_paths repository, path1 is only accessible by jeff, path2 isn’t accessible by any user (since no access control is specified) and path2_explicit explicitly defines the behaviour of path2 (i.e. it isn’t accessible by any user).

app:
  repositories:
    repository:
      write-access:
        users: [ jeff, jack ]
    repository_with_paths:
      write-access:
        users: [ jeff, jack ]
      paths:
        path1:
          write-access:
            users: [ jeff ]
        path2:
        path2_explicit:
          write-access:
            public: false

In this example, jeff and jack have write access to the two repositories (i.e. repository and repository_with_paths). In the repository_with_paths repository, jeff can write to path1, path2 isn’t writable by any user (since no access control is specified) and path2_explicit explicitly defines the behaviour of path2 (i.e. it isn’t writable by any user).

Groups

The groups property grants access to groups of users by listing the group names.

app:
  repositories:
    repository:
      read-access:
        groups: [ SCIENTISTS ]
    repository_with_paths:
      read-access:
        groups: [ SCIENTISTS, MATHEMATICIANS ]
      paths:
        path1:
          read-access:
            groups: [ SCIENTISTS ]

In this example, repository is accessible by any user part of the SCIENTISTS group, and repository_with_paths is accessible by any user part of the SCIENTISTS or MATHEMATICIANS groups. In the repository_with_paths repository, only users part of the SCIENTISTS group can access path1.

app:
  repositories:
    repository:
      write-access:
        groups: [ SCIENTISTS ]
    repository_with_paths:
      write-access:
        groups: [ SCIENTISTS, MATHEMATICIANS ]
      paths:
        path1:
          write-access:
            groups: [ SCIENTISTS ]

In this example, repository is writable by any user part of the SCIENTISTS group, and repository_with_paths is writable by any user part of the SCIENTISTS or MATHEMATICIANS groups. In the repository_with_paths repository, only users part of the SCIENTISTS group can write to path1.

Network

The network property grants access to users when they make request from specific ip-address or ip-ranges.

app:
  repositories:
    repository:
      read-access:
        network: 11.11.11.11
    repository_with_paths:
      read-access:
        network: 22.22.22.22
      paths:
        path1:
          read-access:
            network: 33.33.33.33

In this example repository is accessible for users if their network IP is 11.11.11.11. The repository_with_paths repository is accessible for users from IP address 22.22.22.22. In the repository_with_paths, path1 isn’t accessible to any users as users can’t have multiple IP addresses.

app:
  repositories:
    repository:
      write-access:
        network: 11.11.11.11
    repository_with_paths:
      write-access:
        network: 22.22.22.22
      paths:
        path1:
          write-access:
            network: 33.33.33.33

In this example repository is writable by users if their network IP is 11.11.11.11. The repository_with_paths repository is writable by users from IP address 22.22.22.22. In the repository_with_paths, path1 isn’t accessible to any users as users can’t have multiple IP addresses.

SpEL expression

The expression property makes it possible to create more complex authorizations checks using SpEL expressions. Crane exposes the following properties that you can use:

  • groups: list of groups the user is part of
  • claims: list of claims assigned to the user
app:
  repositories:
    repository1:
      read-access:
        expression: "#{groups.contains('SCIENTISTS') and groups.contains('MATHEMATICIANS')}"
    repository2:
      read-access:
        expression: "#{groups.contains('SCIENTISTS') and !groups.contains('MATHEMATICIANS')}"

In this example repository1 is accessible for users that are part of both the SCIENTISTS and MATHEMATICIANS groups, while repository2 is only accessible for users that are in the SCIENTISTS group and not in the MATHEMATICIANS group.

app:
  repositories:
    repository1:
      write-access:
        expression: "#{groups.contains('SCIENTISTS') and groups.contains('MATHEMATICIANS')}"
    repository2:
      write-access:
        expression: "#{groups.contains('SCIENTISTS') and !groups.contains('MATHEMATICIANS')}"

In this example repository1 is writable by users that are part of both the SCIENTISTS and MATHEMATICIANS groups, while repository2 is only writable by users that are in the SCIENTISTS group and not in the MATHEMATICIANS group.

Any authenticated user

If the any-authenticated-user property is true, the repository is accessible by any authenticated user (but not by unauthenticated users).

app:
  repositories:
    repository:
      read-access:
        any-authenticated-user: true
    repository_with_paths:
      read-access:
        public: true
      paths:
        path1:
          read-access:
            any-authenticated-user: true
        path2:
          read-access:
            any-authenticated-user: false # equivalent to default behavior, can be removed

In this example, repository is accessible for any authenticated user. The repository_with_paths repository is accessible for unauthenticated and authenticated users. In addition, path1 is accessible for authenticated users while path2 isn’t accessible for any user.

app:
  repositories:
    repository:
      write-access:
        any-authenticated-user: true
    repository_with_paths:
      write-access:
        public: true
      paths:
        path1:
          write-access:
            any-authenticated-user: true
        path2:
          write-access:
            any-authenticated-user: false # equivalent to default behavior, can be removed

In this example, repository is writable by any authenticated user. The repository_with_paths repository is writable by unauthenticated and authenticated users. In addition, path1 is writable by authenticated users while path2 isn’t writable by any user.

Restrictions

Crane puts some restrictions to the combination of properties, therefore all of the following isn’t allowed:

  • using public: true on a path in a repository or path that doesn’t have public: true
  • using public: true together with any other access control property in that access control block
  • using any-authenticated-user: true together with the users, groups or expression property

Advanced example

This section summarizes the previous sections using a concrete example config:

app:
  repositories:
    nesting:
      read-access:
        groups: [ SCIENTISTS, MATHEMATICIANS ]
      paths:
        level1:
          read-access:
            groups: [ RESTRICTED ]
          paths:
            level2:
              read-access:
                groups: [ CONFIDENTIAL ]  
            other_level2:
              write-access:
                groups: [ CONFIDENTIAL ]

Scenario 1

Given the preceding config, when a user tries to read the contents of the nesting repository, Crane performs the following checks to determine whether the user has access:

  1. is the user authenticated?
  2. is the user part of the SCIENTISTS or MATHEMATICIANS groups?

If all checks succeeded, the user gets (read) access to the nesting repository.

Scenario 2

Given the preceding config, when a user tries to read the contents of the level1 path, Crane performs the following checks to determine whether the user has access:

  1. is the user authenticated?
  2. is the user part of the SCIENTISTS or MATHEMATICIANS groups?
  3. is the user part of the RESTRICTED group

If all checks succeeded, the user gets (read) access to the level1 repository.

In summary users get read access to level1 if they’re authenticated, part of either the SCIENTISTS or MATHEMATICIANS groups and part of the RESTRICTED group.

Scenario 3

Given the preceding config, when a user tries to read the contents of the other_level2 path, Crane immediately denies the request since other_level2 has no read-access defined.

POSIX

Crane allows the usage of POSIX access control for a storage location. The app.repositories.<REPOSITORY_NAME>.posix-access-control property configures POSIX access control on repository level and is false by default. POSIX is automatically enabled for all paths in the repository. When using POSIX access control, both the path access control and POSIX filesystem permissions are used to determine whether a user has read or write access for a specified directory or file. A user only has access to a directory or file when both access control systems grant access. When it comes to POSIX permissions Crane only uses the read (r) and write (w) permissions of the user or group. The execution (x) and any other permissions aren’t used.

Write permissions

When creating new files with POSIX access control enabled, Crane doesn’t follow the standard rules set by POSIX. If Crane would strictly follow the POSIX standard, new files would only be visible to the user that initiated the upload and members of their primary group. Therefore, Crane uses a simpler and more intuitive approach, overwriting the permissions of newly created files with the permissions of their directory. This way the newly created files are visible to the same users as the directory they’re in.