Ticket #293 (closed Enhancement: fixed)

Opened 8 years ago

Last modified 8 years ago

Backend security

Reported by: jri Owned by: jri
Priority: Critical Milestone: Release 4.1
Component: DeepaMehta Standard Distribution Version: 4.0.11
Keywords: Cc: dgf, Malte
Complexity: 13 Area:
Module:

Description (last modified by jri) (diff)

Each HTTP request must pass a filter chain for authorization. The authorization check must involve the instance's ACLs entries (#262) and the user's authentication status. The user session must be represented by a server-side HTTP session whose ID is stored in a browser cookie. If request authorization fails 401 or 403 must be returned.

Thanks, dgf, for pushing this topic now!
and for the Spring Security hint.

Change History

comment:1 Changed 8 years ago by jri

There is a variety of security frameworks for that purpose. Popular ones include

  • Spring Security

(http://www.springsource.org/spring-security/)

  • Apache Shiro

(http://shiro.apache.org/)

Here is my first impression:

Spring Security is complicated. Perhaps too complicated. At least not very accessible for a first-time (non-Spring) user.

Apache Shiro on the other hand feels very friendly. Easy to understand concepts and clear wording. Very good documentation, also for beginners. For the time being Apache Shiro is clearly my favorite.

But what could render headaches is, again, the OSGi environment. In particular the limits of its HTTP service. The OSGi HTTP service specification neither supports (Servlet API) Filters nor Servlet Context Listeners. While Felix HTTP provides a non-standard extension for Filters (same for Equinox) there is no support for Listeners.

Since version 1.2 Apache Shiro relies on Servlet Context Listeners as the central Shiro SecurityManager? object is created in a Listener. The previous version, Shiro 1.1, in contrast creates the SecurityManager? in a Filter which might work well with Felix.

Things we could consider:

  • Start with Shiro 1.1. Possibly it provides all the features we need for the moment.
  • Investigate how to deploy Shiro 1.2 for OSGi web applications (I saw some not-so-nice approaches already).
  • Ask the Shiro developers for supporting the OSGi web application scenario.
Last edited 8 years ago by jri (previous) (diff)

comment:2 Changed 8 years ago by jri

  • Description modified (diff)

comment:3 Changed 8 years ago by jri

The missing Servlet Context Listeners seems not to be a problem at all. Despite some disadvantages the Shiro SecurityManager? object can be constructed programatically. See http://shiro.apache.org/configuration.html

comment:4 Changed 8 years ago by dgf

Another possibility can be the JSR 250 support of jersey

an short introduction: Jersey JAX-RS, Tomcat, Basic Auth and Security Annotations

a programmatically filter configuration is also supported

class WebPublishingService
....
String[] filters = new String[]{RolesAllowedResourceFilterFactory.class.getName()};
rootApplication.getProperties().put(ResourceFilters.class.getName(), filters);
jerseyServlet = new ServletContainer(rootApplication);

comment:5 Changed 8 years ago by jri

Thanks, dgf, for that hint!
The Jersey filters open a set of new possibilities.
Perhaps we can start even without using a security framework.

My next goal is securing the application as whole:
1) Starting the Webclient must require to login. That is, the very first request must result in 401 if not logged in.
This requires securing the static resources (scripts). As these are not delivered by Jersey (but Jetty directly) I will try to setup a Filter (Servlet API, not Jersey API) via Felix's ExtHttpService?.
2) For securing the REST resources I will try Jersey Container Filters. All REST resources must accessible only for logged in users.

Finer granularity (ACL-based at instance-level) could be realized later on by the Jersey Resource Filters you mentioned.

Thanks, dgf, for the phone discussion!

comment:6 Changed 8 years ago by Jörg Richter

Core: try out Jersey Container Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:7 Changed 8 years ago by Jörg Richter

Core: try out Servlet Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:8 Changed 8 years ago by Jörg Richter

Core: new setting dm4.installation.type (#293).

There are 4 DeepaMehta installation types (left column):

                    Net Interface       Read                Write
------------------------------------------------------------------------
SINGLE-USER         localhost           yes                 yes
(default)

PRIVATE             all                 if logged in        if logged in

PUBLIC              all                 yes                 if logged in

SANDBOX             all                 yes                 yes

See ticket 293.

comment:9 Changed 8 years ago by Jörg Richter

comment:10 Changed 8 years ago by Jörg Richter

Access Control: fix cyclic dependency (#293).

The request filter is setup by the Access Control plugin (instead by DM4 Core).

Changed in Access Control service:

  • login() renamed to checkCredentials()
  • getUsername() has no parameter anymore ("clientState" is dropped)
  • getTopicPermissions() has no "clientState" parameter anymore

Furthermore in DM4 Core: a plugin can register a (Servlet API) filter by calling

  • registerFilter(Filter filter) declared in plugin base class

See ticket 293.

comment:11 Changed 8 years ago by Jörg Richter

Access Control: create HTTP sessions (#293).

For a logged in user a HTTP session is created.
The dm4_username cookie is not used anymore.

Furthermore in Webclient: the API method require_login() is removed.

See ticket 293.

comment:12 Changed 8 years ago by Jörg Richter

Access Control: logout (#293).

Once logged out the HTTP session is invalidated.

Changes in Access Control service:

  • logout() is added
  • lookupUserAccount() is dropped

See ticket 293.

comment:13 Changed 8 years ago by Jörg Richter

Access Control: drop "installation type" (#293).

Instead the "dm4.installation.type" config property there are 2 separate boolean properties in global pom.xml:

  • dm4.security.read_requires_login # default is "false"
  • dm4.security.write_requires_login # default is "true"

See ticket 293.

comment:14 Changed 8 years ago by Jörg Richter

Access Control: logout shuts down client (#293).

When logging out from a "private" DM installation the webclient shuts down, that is the browser window is emptied. This reflects the meaning of a "private" installation: the webclient is only accessible when logged in.

A DM installation is made "private" by setting the config property dm4.security.read_requires_login to true (in global pom.xml):

    <dm4.security.read_requires_login>true</dm4.security.read_requires_login>

Note this drawback: the next request logs in the user automatically. This is a known problem with HTTP Basic authentication.

See ticket 293.

comment:15 Changed 8 years ago by Jörg Richter

Access Control: global subnet filter (#293).

The subnet filter of the Files plugin is moved to the Access Control plugin.
The subnet check is applied to *every* request (not just file requests).

There is a new config property in global pom.xml:

    <dm4.security.subnet_filter>127.0.0.1/32</dm4.security.subnet_filter>

The former dm4.filerepo.netfilter property is dropped.

See ticket 293.

comment:16 Changed 8 years ago by Jörg Richter

Core: try out Jersey Container Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:17 Changed 8 years ago by Jörg Richter

Core: try out Servlet Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:18 Changed 8 years ago by Jörg Richter

Core: new setting dm4.installation.type (#293).

There are 4 DeepaMehta installation types (left column):

                    Net Interface       Read                Write
------------------------------------------------------------------------
SINGLE-USER         localhost           yes                 yes
(default)

PRIVATE             all                 if logged in        if logged in

PUBLIC              all                 yes                 if logged in

SANDBOX             all                 yes                 yes

See ticket 293.

comment:19 Changed 8 years ago by Jörg Richter

comment:20 Changed 8 years ago by Jörg Richter

Access Control: fix cyclic dependency (#293).

The request filter is setup by the Access Control plugin (instead by DM4 Core).

Changed in Access Control service:

  • login() renamed to checkCredentials()
  • getUsername() has no parameter anymore ("clientState" is dropped)
  • getTopicPermissions() has no "clientState" parameter anymore

Furthermore in DM4 Core: a plugin can register a (Servlet API) filter by calling

  • registerFilter(Filter filter) declared in plugin base class

See ticket 293.

comment:21 Changed 8 years ago by Jörg Richter

Access Control: create HTTP sessions (#293).

For a logged in user a HTTP session is created.
The dm4_username cookie is not used anymore.

Furthermore in Webclient: the API method require_login() is removed.

See ticket 293.

comment:22 Changed 8 years ago by Jörg Richter

Access Control: logout (#293).

Once logged out the HTTP session is invalidated.

Changes in Access Control service:

  • logout() is added
  • lookupUserAccount() is dropped

See ticket 293.

comment:23 Changed 8 years ago by Jörg Richter

Access Control: drop "installation type" (#293).

Instead the "dm4.installation.type" config property there are 2 separate boolean properties in global pom.xml:

  • dm4.security.read_requires_login # default is "false"
  • dm4.security.write_requires_login # default is "true"

See ticket 293.

comment:24 Changed 8 years ago by Jörg Richter

Access Control: logout shuts down client (#293).

When logging out from a "private" DM installation the webclient shuts down, that is the browser window is emptied. This reflects the meaning of a "private" installation: the webclient is only accessible when logged in.

A DM installation is made "private" by setting the config property dm4.security.read_requires_login to true (in global pom.xml):

    <dm4.security.read_requires_login>true</dm4.security.read_requires_login>

Note this drawback: the next request logs in the user automatically. This is a known problem with HTTP Basic authentication.

See ticket 293.

comment:25 Changed 8 years ago by Jörg Richter

Access Control: global subnet filter (#293).

The subnet filter of the Files plugin is moved to the Access Control plugin.
The subnet check is applied to *every* request (not just file requests).

There is a new config property in global pom.xml:

    <dm4.security.subnet_filter>127.0.0.1/32</dm4.security.subnet_filter>

The former dm4.filerepo.netfilter property is dropped.

See ticket 293.

comment:26 Changed 8 years ago by jri

  • Status changed from new to closed
  • Resolution set to fixed

Summary:
The backend (its REST API) is secured at a coarse level: each request must pass a filter. The filter is controlled by 3 config properties:

dm4.security.read_requires_login       # default: false
dm4.security.write_requires_login      # default: true
dm4.security.subnet_filter             # default: 127.0.0.1/32

This is sufficient e.g. to deploy a shared private DM installation -> set both flags to ”true" and the netfilter to "0.0.0.0/0".
No (per-instance) ACLs are involved at this level.
For the moment this is considered sufficient and the ticket is now closed.

comment:27 Changed 8 years ago by Jörg Richter

Access Control: login is a POST request (#293).

Login via REST API now uses Basic Authentication.

To login:

    POST /accesscontrol/login

Put username and passwort in the "Authorization" header, according to Basic Authentication:
The string "Basic " appended by the Base64 encoded form of "{username}:{password}".

Note: no RequestFilter? rule exception is required. The POST request is autorized on-the-fly via Basic Authentication.
(Basic Authentication allows to send the "Authorization" header along with the initial request, not forcing a 401).

Further changes in Webclient:
The RESTClient's public request() method takes a "headers" object (replacing the "content_type" parameter).
A plugin can pass arbitrary request headers.

    this.request = function(method, uri, data, headers)

See ticket 293.

comment:28 Changed 8 years ago by Jörg Richter

Access Control: login is a POST request (#293).

Login via REST API now uses Basic Authentication.

To login:

    POST /accesscontrol/login

Put username and passwort in the "Authorization" header, according to Basic Authentication:
The string "Basic " appended by the Base64 encoded form of "{username}:{password}".

Note: no RequestFilter? rule exception is required. The POST request is autorized on-the-fly via Basic Authentication.
(Basic Authentication allows to send the "Authorization" header along with the initial request, not forcing a 401).

Further changes in Webclient:
The RESTClient's public request() method takes a "headers" object (replacing the "content_type" parameter).
A plugin can pass arbitrary request headers.

    this.request = function(method, uri, data, headers)

See ticket 293.

comment:29 Changed 8 years ago by Jörg Richter

Core: try out Jersey Container Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:30 Changed 8 years ago by Jörg Richter

Core: try out Servlet Filters (#293).

Just an experiment. Nothing useful for the moment.

See ticket 293.

comment:31 Changed 8 years ago by Jörg Richter

Core: new setting dm4.installation.type (#293).

There are 4 DeepaMehta installation types (left column):

                    Net Interface       Read                Write
------------------------------------------------------------------------
SINGLE-USER         localhost           yes                 yes
(default)

PRIVATE             all                 if logged in        if logged in

PUBLIC              all                 yes                 if logged in

SANDBOX             all                 yes                 yes

See ticket 293.

comment:32 Changed 8 years ago by Jörg Richter

comment:33 Changed 8 years ago by Jörg Richter

Access Control: fix cyclic dependency (#293).

The request filter is setup by the Access Control plugin (instead by DM4 Core).

Changed in Access Control service:

  • login() renamed to checkCredentials()
  • getUsername() has no parameter anymore ("clientState" is dropped)
  • getTopicPermissions() has no "clientState" parameter anymore

Furthermore in DM4 Core: a plugin can register a (Servlet API) filter by calling

  • registerFilter(Filter filter) declared in plugin base class

See ticket 293.

comment:34 Changed 8 years ago by Jörg Richter

Access Control: create HTTP sessions (#293).

For a logged in user a HTTP session is created.
The dm4_username cookie is not used anymore.

Furthermore in Webclient: the API method require_login() is removed.

See ticket 293.

comment:35 Changed 8 years ago by Jörg Richter

Access Control: logout (#293).

Once logged out the HTTP session is invalidated.

Changes in Access Control service:

  • logout() is added
  • lookupUserAccount() is dropped

See ticket 293.

comment:36 Changed 8 years ago by Jörg Richter

Access Control: drop "installation type" (#293).

Instead the "dm4.installation.type" config property there are 2 separate boolean properties in global pom.xml:

  • dm4.security.read_requires_login # default is "false"
  • dm4.security.write_requires_login # default is "true"

See ticket 293.

comment:37 Changed 8 years ago by Jörg Richter

Access Control: logout shuts down client (#293).

When logging out from a "private" DM installation the webclient shuts down, that is the browser window is emptied. This reflects the meaning of a "private" installation: the webclient is only accessible when logged in.

A DM installation is made "private" by setting the config property dm4.security.read_requires_login to true (in global pom.xml):

    <dm4.security.read_requires_login>true</dm4.security.read_requires_login>

Note this drawback: the next request logs in the user automatically. This is a known problem with HTTP Basic authentication.

See ticket 293.

comment:38 Changed 8 years ago by Jörg Richter

Access Control: global subnet filter (#293).

The subnet filter of the Files plugin is moved to the Access Control plugin.
The subnet check is applied to *every* request (not just file requests).

There is a new config property in global pom.xml:

    <dm4.security.subnet_filter>127.0.0.1/32</dm4.security.subnet_filter>

The former dm4.filerepo.netfilter property is dropped.

See ticket 293.

comment:39 Changed 8 years ago by Jörg Richter

Access Control: login is a POST request (#293).

Login via REST API now uses Basic Authentication.

To login:

    POST /accesscontrol/login

Put username and passwort in the "Authorization" header, according to Basic Authentication:
The string "Basic " appended by the Base64 encoded form of "{username}:{password}".

Note: no RequestFilter? rule exception is required. The POST request is autorized on-the-fly via Basic Authentication.
(Basic Authentication allows to send the "Authorization" header along with the initial request, not forcing a 401).

Further changes in Webclient:
The RESTClient's public request() method takes a "headers" object (replacing the "content_type" parameter).
A plugin can pass arbitrary request headers.

    this.request = function(method, uri, data, headers)

See ticket 293.

Note: See TracTickets for help on using tickets.