Skip to main content

Introduction

note

This guide assumes familiarity with the Spring Framework.
If you are new to Spring, we recommend starting with their official guides to get up to speed.

Singularity provides a very flexible framework to create custom content objects. Articles come out-of-the-box.

Configuration

You need to set the URL pattern for content in your frontend.

PropertyTypeDescriptionDefault value
singularity.content.content-urlStringThe URL of content objects in your frontend. Please use the placeholders {contentType} and {contentKey} which will be autofilled by the application.http://localhost:4200/content/{contentType}/{contentKey}

Let's say you set this property to https://example.com/content/{contentType}/{contentKey}. The article with a key of my-cool-article should be available in your frontend on the url https://example.com/content/articles/my-cool-article.

Core Content Object Structure

Every piece of Content in the system is an abstract object that implements the ContentDocument<T> interface. It contains not only the access configuration but also crucial metadata for identification, versioning, and security.

The following fields are common to all content types (e.g., Article, FileMetadata):

FieldTypeDescription
idObjectIdThe unique internal MongoDB ID for the document.
keyStringThe unique, stable key used to identify and retrieve the object within the system.
createdAtInstantThe timestamp when the content object was first created.
updatedAtInstantThe timestamp of the last modification to the content object.
trustedBooleanA server-admin controlled flag, crucial for security (e.g., ensuring integrity for critical links or files).
tagsMutableSet<String>Tags used for filtering, categorization, and discovery. You can learn more here.
accessContentAccessDetailsThe complete object detailing ownership, visibility, and explicit sharing permissions.

Detailed Access Structure

The core of the access control is encapsulated in the ContentAccessDetails object, which is part of every ContentDocument.

FieldTypeDescription
ownerIdObjectIdThe unique ID of the user who owns the content. The Owner implicitly has MAINTAINER rights.
visibilityAccessTypeDefines the initial access state: PRIVATE, PUBLIC, or SHARED. Defaults to PRIVATE.
usersContentAccessPermissionsStores explicit roles (MAINTAINER, EDITOR, VIEWER) granted to individual User IDs.
groupsContentAccessPermissionsStores explicit roles (MAINTAINER, EDITOR, VIEWER) granted to Group IDs.
invitationsMutableSet<ObjectId>A list of pending invitation document IDs linked to this content object.
Invitations

You can learn more about invitations here.

ContentAccessPermissions

This object is used by both the users and groups fields to manage explicit, non-owner permissions.

The roles are implemented as separate sets of Subject IDs (User IDs or Group IDs), which ensures that roles are additive (a maintainer is also an editor and viewer) and mutually exclusive (a subject can only be in one set at a time):

data class ContentAccessPermissions(
val maintainer: MutableSet<String>,
val editor: MutableSet<String>,
val viewer: MutableSet<String>
)
note

When a role is assigned via the put() function, any existing role for that subject is explicitly removed first, confirming that each subject can only hold the highest explicit role they were granted.

Access States

The Access field determines the initial visibility of a Content object:

StateVisibility & RuleDescription
PUBLICVisible to all users and potentially anonymous users.No explicit permission check is required for viewing.
PRIVATEVisible only to the Owner.The default state. All other access attempts are denied.
SHAREDVisible to the Owner and explicitly invited Users or Groups.Access is determined by the Object-Specific Roles assigned to the invited user and groups.

Object-Specific Roles (Shared State)

When a Content object is in the Shared state, the Owner and Maintainers can grant specific roles to individual Users or Groups. These roles define the level of interaction with the object.

RolePermissionsManagement Rights
VIEWERRead ContentNone
EDITORRead Content, Modify/Edit ContentNone
MAINTAINERRead, Modify/Edit Content, Invite/Remove Users/Groups, Manage Access, Delete ObjectFull management rights over the object.

Owner's Privileges

The Owner is the highest authority over a Content object:

  • The Owner always possesses all rights of a Maintainer.
  • Owner Change: When the ownership is transferred to a new user, the old owner is automatically demoted to a Maintainer role to ensure continuity of management.

Authorization Logic

The primary access function (hasAccess(authentication, role)) determines if an authenticated user meets the required role for a given action. The final permission is granted if any of the following conditions are met:

Required RoleAccess Granted if...
MAINTAINER1. The user has the ADMIN global role. OR 2. The user/group is explicitly a MAINTAINER.
EDITOR1. The user has the ADMIN global role. OR 2. The user/group is explicitly a MAINTAINER. OR 3. The user/group is explicitly an EDITOR.
VIEWER1. The user has the ADMIN global role. OR 2. The content is PUBLIC. OR 3. The user/group is explicitly a MAINTAINER, EDITOR, or VIEWER.

Group Access Checking

When checking a user's access, the system iterates over all groups the user belongs to and grants the highest role found across all explicit user roles and group roles.

Global Server Group: Contributor

A dedicated server-wide group, renamed to Contributor, manages the creation of new objects and tags.

Group NameCore PermissionsScope
contributorCreate new Content Objects, Create new TagsServer-wide permission to initiate content.
info

While members of the Contributor group can create new Content, they only gain access to resources they personally created or to which they have been explicitly granted access via the Shared state. This separation ensures that creation rights do not automatically grant read/write access to all existing content.