This section provides comprehensive information about the security measures implemented within.  Specifically, authentication, encryption and role management, each section will detail its configuration, attributes, and if it applies, its usage inside the code with examples.

Example Configuration

GAIA API configuration can be found in config/config.py

CONFIG = {
    .
    .
    .
    # *******************************************************************************
    # Security Configuration
    # *******************************************************************************
    'security': {
	    'authentication': ...,
 	    'encryption': ..., 
        'roles': ...,  
		'headers': ...,
     }
    .
    .
    .
}

Authentication

GAIA-API leverages the power of JSON Web Tokens (JWT) to secure all communication between clients and the API. JWT is an open standard for securely transmitting information between parties as a JSON object. It consists of a header, a payload, and a signature, which are digitally signed and encoded to form a compact and self-contained token.

The use of JWT provides several advantages in terms of security for our API. It ensures the integrity of data by digitally signing each token, making it tamper-evident. Additionally, JWT allows for stateless authentication, meaning the server does not need to store any session information.

We highly recommend that you familiarize yourself with the security guidelines presented here to ensure the proper implementation and usage of GAIA. By following these guidelines, you can maximize the security of your API interactions and protect the confidentiality and integrity of your data.


Configuration

PropertyDescriptionDefaultTypeRequired
enabledIndicates whether authentication needs to be implemented or notfalsebooleanNo
typeIndicates the type of authentication to implement
AuthenticationType.LOCAL
stringNo
secretThe secret used to sign and decrypt the JWT. Does not apply with Delegated52ecfd60e01b800355a8ce59780f9243b4662c3a236394eestring/objectNo

force_https_original_url_redirection

Force the protocol to https for all original url redirections, when doing authenticationfalsebooleanNo
anonymousIndicates the body of the Anonymous user in case of no authentication{"id": "Anonymous", "account": "[email protected]", "name": "Anonymous", "displayName": "Anonymous"}objectNo
localLocal Authentication
LocalNo
ldapLDAP Authentication
LDAPNo
samlSAML Authentication
SAMLNo
oidcOIDC Authentication
OIDCNo
delegatedJWKS Authentication
DelegatedNo

Authentication Types

To select a 'type' inside the security configuration you need to enter any of these options as using just strings is deprecated:

  • AuthenticationType.LOCAL
  • AuthenticationType.LDAP
  • AuthenticationType.OIDC
  • AuthenticationType.DELEGATED

Example Configuration

CONFIG = {
    # *******************************************************************************
    'security': {
        'authentication': {
            'enabled': False,
            'type': AuthenticationType.OIDC,
            'secret': os.getenv('AUTH_SECRET', default='52ecfd60e01b800355a8ce59780f9243b4662c3a236394ee'),
            'force_https_original_url_redirection': False,

            'anonymous': {
                'id': 'Anonymous',
                'account': '[email protected]',
                'name': 'Anonymous',
                'displayName': 'Anonymous'
            },

            'local': {
                ...
            },

            'delegated': {
                ...
            },

            'ldap': {
                ...
            },

            'oidc': {
                ...
            }
         }
    }     
}


Attributes Mapping 

Almost every method available in GAIA-API has a property called attributesMapping, this property is used to remap any value in the user data provided by the authentication service into the User data available throughout the server. This transformation is achieved by mapping each key:value pair, where the value is looked up as a key in the user data from the service and remapped with the key in the User data for the server. The original key is kept, as well as any other unspecified values.

The only required attributes for the attributesMapping are id and account

PropertyDescriptionDefaultTypeRequired
idUnique identifier for the user
stringYes
accountUser account, usually the same one used for login
stringYes
rolesUser roles, used for access to different sections in the API and UI[]arrayNo

if the attribute roles is remapped, or already exist in the available data, the respective roles will be assigned or will create Roles object managed by the RoleManager

Cookie Configuration 

PropertyDescriptionDefaultTypeRequired
nameThe name of the cookie used for SAML session management.
stringNo
domainThe domain of the cookie used for SAML session management.
stringNo
pathThe path of the cookie used for SAML session management./stringNo
secureWhether the cookie used for SAML session management should only be sent over HTTPS.falsebooleanNo
httpOnlyWhether the cookie used for SAML session management should be accessible only through the HTTP protocol.truebooleanNo
sameSiteSpecifies the SameSite attribute value of the cookie used for SAML session management.NonestringNo

Methods

Local 

This authentication method uses a CSV file, which involves storing user credentials in a structured format for authentication purposes. In this case, we have a CSV file with the following headers: id, account, password, email, roles, and disabled.

  • ID: Unique ID for the user
  • Account: User credential necessary for the login
  • Password: User password
  • Email: User Email 
  • Roles: List of different role names assigned to each user
  • Disabled: Boolean indicating whether the user account is disabled

It's important to note that using a CSV file for authentication has limitations and may not be suitable for production systems with large user bases. However, it can serve as a simple example or proof of concept. In practice, more robust and secure methods, such as using databases, user directories, or authentication services, are typically employed for user authentication.

Configuration

PropertyDescriptionDefaultTypeRequired
fileCSV file with the definition of the user to have access to the server
stringYes


Example Configuration

'local': {
 	'file': '/path/to/user.csv'
}


Example CSV File

id,account,password,email,roles,disabled
1,user1,pass123,[email protected],"role1, role2",false
2,user2,pass456,[email protected],"role2, role3",true

There is a CSV file in SearchAPI located in config/auth/users.csv


LDAP 

LDAP (Lightweight Directory Access Protocol) is a widely adopted protocol for accessing and managing directory information services. It is a lightweight, platform-independent, and network-oriented protocol designed for directory services such as user authentication, authorization, and directory information retrieval.

LDAP is based on a client-server model, where a client application interacts with an LDAP server to perform various directory operations. The LDAP server acts as a centralized repository that stores and manages directory entries, which typically represent users, groups, devices, or other resources within an organization.

Configuration

PropertyDescriptionDefaultTypeRequired
urlThe LDAP server URL. It specifies the network address and protocol (e.g., ldap:// or ldaps://) for connecting to the LDAP server.os.getenv('LDAP_URL')stringYes
authenticationThe LDAP authentication method.SIMPLEstringYes
bindDNContains the Distinguished Name (DN) used for binding or authenticating with the LDAP server.
stringYes
bindCredentialsContains the password or credentials associated with the bindDN.os.getenv('LDAP_CREDENTIALS')stringYes
searchBaseSpecifies the base DN from which LDAP searches should begin.
stringYes
searchFilterRepresents the LDAP search filter used to narrow down the search results based on specific criteria.
stringYes
searchAttributesSpecifies the attributes to be returned in the search results.
arrayNo
tlsOptionsContains TLS (Transport Layer Security) options for establishing a secure connection with the LDAP server.
objectNo
bindPropertySpecifies the LDAP attribute used for binding or authenticating with the LDAP server.
stringNo
groupDnPropertyRepresents the LDAP attribute used to identify the distinguished name (DN) of a group.
stringNo
groupSearchBaseSpecifies the base DN from which group searches should begin.
stringNo
groupSearchFilterRepresents the LDAP search filter used to narrow down the search results for groups based on specific criteria.
stringNo
attributesMappingAttribute Mapping, remaps any value in the user data provided by the authentication service, into the User data available throughout the server.
AttributesMappingYes

Example Configuration

'ldap': {
  "url": os.getenv('LDAP_URL', default="ldap://ldap.example.com"),
  "authentication": "SIMPLE",
  "bindDN": "cn=admin,dc=example,dc=com",
  "bindCredentials": os.getenv('LDAP_CREDENTIALS'),
  "searchBase": "ou=users,dc=example,dc=com",
  "searchFilter": "(objectClass=person)",
  "searchAttributes": ["cn", "email"],
  "tlsOptions": {
    "certificateVerification": true,
    "cipherSuites": ["TLS_RSA_WITH_AES_256_CBC_SHA256"],
    "clientCertificate": "/path/to/client_certificate.pem",
    "clientKey": "/path/to/client_key.pem"
  },
  "bindProperty": "uid",
  "groupDnProperty": "memberOf",
  "groupSearchBase": "ou=groups,dc=example,dc=com",
  "groupSearchFilter": "(objectClass=group)",
  "attributesMapping": {
    "id": "uid",
    "account": "sAMAccountName",
    "roles": "client_roles"
   }
}


SAML 

Removed in version 2.0


SAML2 (Security Assertion Markup Language 2.0) authentication is a standard protocol used for exchanging authentication and authorization data between a Service Provider (SP) and an Identity Provider (IdP). It enables secure single sign-on (SSO) functionality, allowing users to authenticate once with the IdP and access multiple services provided by different SPs without the need to re-enter credentials.

Configuration

PropertyDescriptionDefaultTypeRequired
strictIf strict is True, then the Python Toolkit will reject unsigned or unencrypted messages if it expects them to be signed or encrypted. Also, it will reject the messages if the SAML standard is not strictly followed. Destination, NameId, Conditions, etc. are validated too.falsebooleanNo
debugControls the debugging mode. When set to True, additional debug information will be logged during SAML processing.falsebooleanNo
entity_idGlobally unique identifier for the entity and is typically a URL that represents the identity provider or service provider.
stringYes
single_sign_on_service_urlThe single SignOn Service URL for SAML2.
stringYes
single_sign_on_service_bindingThe single SignOn Service Binding for SAML2.urn:oasis:names:tc:SAML:2.0:bindings:HTTP-RedirectstringNo
single_logout_service_urlThe single Logout Service URL for SAML2.
stringYes
single_logout_service_bindingThe single Logout Service Binding for SAML2.urn:oasis:names:tc:SAML:2.0:bindings:HTTP-RedirectstringNo
callback_urlThe callback URL for SAML authentication.http://localhost:8085/es/auth/saml/callbackstringNo
x509cert_pathPath to the public X.509 certificate of the Service Provider, used for signing requests or encrypting assertions. It should be provided in PEM format.
stringNo
x509certThe raw public X.509 certificate of the Service Provider, used for signing requests or encrypting assertions.
stringNo
cert_fingerprintInstead of using the whole X.509 cert, you can use a fingerprint in order to validate a SAMLResponse (but you still need the X.509 cert to validate LogoutRequest and LogoutResponse using the HTTP-Redirect binding).
stringNo
cert_fingerprint_algorithmAlgorithm to use in the cert_fingerprint.sha1stringNo
securitySAMLAuthSecurityConfig (Declared below)
SAMLAuthSecurityConfigNo
cookieCookie configuration to store the JWT with the current session.
CookieConfigNo
attributesMappingAttribute Mapping, remaps any value in the user data provided by the authentication service into the User data available throughout the server
AttributesMappingNo

json_redirect

Redirect to the third party sent as a JSON response. Useful when working with (single-page application)falsebooleanNo

SAML Security Configuration

PropertyDescriptionDefaultTypeRequired
nameIdEncryptedIndicates that the nameID of the <samlp:logoutRequest> sent by this SP will be encrypted.falsebooleanNo
authnRequestsSignedIndicates whether the <samlp:AuthnRequest> messages sent by this SP will be signed. [Metadata of the SP will offer this info]falsebooleanNo
logoutRequestSignedIndicates whether the <samlp:logoutRequest> messages sent by this SP will be signed.falsebooleanNo
logoutResponseSignedIndicates whether the <samlp:logoutResponse> messages sent by this SP will be signed.falsebooleanNo
signMetadataSign the Metadatafalseboolean or objectNo
wantMessagesSignedIndicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>, and <samlp:LogoutResponse> elements received by this SP to be signed.falsebooleanNo
wantAssertionsSignedIndicates a requirement for the <saml:Assertion> elements received by this SP to be signed. [Metadata of the SP will offer this info]falsebooleanNo
wantAssertionsEncryptedIndicates a requirement for the <saml:Assertion> elements received by this SP to be encrypted.falsebooleanNo
wantNameIdIndicates a requirement for the NameID element on the SAMLResponse received by this SP to be present.truebooleanNo
wantNameIdEncryptedIndicates a requirement for the NameID received by this SP to be encrypted.falsebooleanNo
wantAttributeStatementIndicates a requirement for the AttributeStatement element.truebooleanNo
requestedAuthnContextSet to false and no AuthContext will be sent in the AuthNRequest, Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'. Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509')truebooleanNo
requestedAuthnContextComparisonAllows the authn comparison parameter to be set, defaults to 'exact' if the setting is not present.exactstringNo
failOnAuthnContextMismatchSet to true to check that the AuthnContext(s) received match(es) the requested.falsebooleanNo
metadataValidUntilProvide the desired TimeStamp, for example 2015-06-26T20:00:00Zstring
No
metadataCacheDurationProvide the desired Duration, for example PT518400S (6 days)string
No
allowSingleLabelDomainsIf enabled, URLs with single-label-domains will be allowed and not rejected by the settings validator (Enable it under Docker/Kubernetes/testing env, not recommended on production)falsebooleanNo
signatureAlgorithmAlgorithm that the toolkit will use on the signing process.http://www.w3.org/2001/04/xmldsig-more#rsa-sha256stringNo
digestAlgorithmAlgorithm that the toolkit will use on the digest process.http://www.w3.org/2001/04/xmlenc
No


Example Configuration

'saml': {
  "strict": False,
  "debug": False,
  "json_redirect": False,
  "entity_id": "https://your-idp.com/entity",
  "single_sign_on_service_url": "https://your-idp.com/sso",
  "single_sign_on_service_binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
  "single_logout_service_url": "https://your-idp.com/slo",
  "single_logout_service_binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
  "callback_url": "http://your-app.com/auth/saml/callback",
  "x509cert_path": "/path/to/certificate.pem",
  "x509cert": "-----BEGIN CERTIFICATE-----\n...certificate content...\n-----END CERTIFICATE-----",
  "cert_fingerprint": "AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01",
  "cert_fingerprint_algorithm": "sha256",
  "security": {
    "nameIdEncrypted": False,
    "authnRequestsSigned": False,
    "logoutRequestSigned": False,
    "logoutResponseSigned": False,
    "signMetadata": False,
    "wantMessagesSigned": False,
    "wantAssertionsSigned": False,
    "wantAssertionsEncrypted": False,
    "wantNameId": True,
    "wantNameIdEncrypted": False,
    "wantAttributeStatement": True,
    "requestedAuthnContext": True,
    "requestedAuthnContextComparison": "exact",
    "failOnAuthnContextMismatch": False,
    "metadataValidUntil": "2023-12-31T23:59:59Z",
    "metadataCacheDuration": "PT24H",
    "allowSingleLabelDomains": False,
    "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
    "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",
    "allowRepeatAttributeName": False,
    "rejectDeprecatedAlgorithm": True
  },
  "cookie": {
    "max_age": 12,
    "expires": 12,
    "path": "/es",
    "domain": os.getenv("COOKIE_DOMAIN_NAME"),
    "secure": False,
    "httponly": False,
    "samesite": "lax"
  },
  "attributesMapping": {
    "id": "userID",
    "account": "email",
    "roles": "client_roles"
   }
}

OIDC 

OIDC (OpenID Connect) authentication is an authentication protocol built on top of the OAuth 2.0 framework. It provides a standardized way for clients (relying parties) to authenticate users by leveraging an Identity Provider (IdP).Configuration

Configuration

PropertyDescriptionDefaultTypeRequired
client_idA unique identifier assigned to the client application by the IdP. It identifies the client application during authentication and authorization requests.os.getenv('OIDC_CLIENT_ID')stringYes
openid_configuration_uriURI to the OpenID Connect configuration values from the provider's Well-Known Configuration Endpoint, (e.g. https://your-provider.com/.well-known/openid-configuration)os.getenv('OIDC_OPENID_CONFIG_URI')stringYes
callback_urlThe URL to which the IdP will redirect the user after successful authentication. It must match one of the registered redirect URIs for the client application.http://localhost:8085/es/auth/oidc/callbackstringNo
scopeDefines the permissions or claims requested by the client application. It may include values like openid (required for OIDC), profile, email, offline_access, or custom scopes defined by the IdP.["openid", "email", "profile"]array of stringsNo
cookieCookie configuration to store the JWT with the current session
CookieConfigYes
attributesMappingAttribute Mapping, remaps any value in the user data provided by the authentication service, into the User data available throughout the server. The way it serves is for each key:value looks for the value as a key in the user data from the service, and remaps it with the key in the User data for the server. The original key is kept as well as any other non-specified value.
AttributesMappingYes

disable_jwt_verification

Disables the internal certificate verification for JWT.  Only set to False if you get an error during initialization:


jwt.exceptions.PyJWKClientConnectionError: Fail to fetch data from the url, err:

"<urlopen error [[SSL: CERTIFICATE_VERIFY_FAILED]] certificate verify failed:

certificate has expired (_ssl.c:1006)>"

falsebooleanNo

json_redirect

Redirect to the third party sent as a JSON response. Useful when working with (single-page application)falsebooleanNo

Example Configuration

'oidc': {
  "client_id": os.getenv('OIDC_CLIENT_ID'),
  "openid_configuration_uri": os.getenv('OIDC_OPENID_CONFIG_URI'),
  "callback_url": "http://localhost:8085/es/auth/oidc/callback",
  "disable_jwt_verification": False, 
  "json_redirect": False,
  "scope": ["openid", "email", "profile"],
  "cookie": {
    "max_age": 12,
    "expires": 12,
    "path": "/es",
    "domain": os.getenv("COOKIE_DOMAIN_NAME"),
    "secure": False,
    "httponly": False,
    "samesite": "lax"
  },
  "attributesMapping": {
    "id": "sub",
    "account": "email",
    "roles": "client_roles"
   }
}

Delegated (JWKS) 

Delegated is a JWT validation with JWKS (JSON Web Key Set) involves verifying the authenticity and integrity of a JWT by using public keys obtained from the IdP's JWKS endpoint.By utilizing JWKS for JWT validation, the client can ensure that the token was issued by a trusted party (IdP) and has not been tampered with. The use of public keys enables cryptographic verification, as only the corresponding private key held by the IdP can produce a valid signature for the JWT.

Configuration

PropertyDescriptionDefaultTypeRequired
debugDebug mode flagfalsebooleanNo
jwks_urlJWKS (JSON Web Key Set) URL is a location where public keys used for verifying JSON Web Tokens (JWTs) can be retrieved (e.g. "https://your-idp.com/.well-known/jwks.json")os.getenv('DELEGATE_JWKS_URL')stringNo
audienceIt specifies the entity or service for which the JWT is issued. The audience claim helps ensure that JWTs are only accepted by the intended recipients, providing an extra layer of security
stringNo
attributesMappingAttribute Mapping, remaps any value in the user data provided by the authentication service into the User data available throughout the server
AttributesMappingYes

Example Configuration

'delegated': {
  "debug": False,
  "jwks_url": os.getenv('DELEGATE_JWKS_URL'),
  "audience": "your-service-audience",
  "attributesMapping": {
    "id": "sub",
    "account": "email",
    "roles": "client_roles"
  }
}

When using AZURE usually the JWKS_URL will have this format: https://login.microsoftonline.com/<Azure Tenant ID>/discovery/v2.0/keys and the AUDIENCE will be the client id of the application registration you will be authorizing. With those 2 values you will have something like this:


'delegated': {
	'jwks_url': f"https://login.microsoftonline.com/{os.getenv('TENANT')}/discovery/v2.0/keys",
	'audience': os.getenv('AUDIENCE'), #ClientID
	'attributesMapping': {
		# key is the property name stored in the SEIA user profile,
		# the value is the user attribute in LDAP

		'id': 'appid',  # _id is required
		'account': 'appid'  # account is for roles and group expansion
	}

}


Where TENANT and AUDIENCE will look similar to this:

os.environ['TENANT'] = os.getenv('TENANT', default="pawd123-1231-12a3-89ab-123a12a56a00")
os.environ['AUDIENCE'] = os.getenv('AUDIENCE', default="1a2f2e2-01ac-4abc-1234-1234abc89a1b") 

Make sure to remove the authentication methods you are not using, like the OIDC and the LDAP blocks.

Encryption

The Encryption section of the SearchAPI project documentation provides an overview of the encryption functionalities implemented within the project. Encryption is a vital aspect of securing sensitive data, ensuring its confidentiality and integrity.

This section focuses on two essential functions: encrypt and decrypt. These functions are part of the utils.crypto module and provide convenient encryption and decryption capabilities for developers working on the SearchAPI project.

Configuration

PropertyDescriptionTypeRequired
secret_keyPiece of information or parameter that is used to encrypt and decrypt messages in a symmetric. The size of the secret key should be no less than 32 characters. If no secret key is specified, one will be created in the config/auth folderstring/file-pathYes

Example Configuration

This section provides an overview of the encryption mechanisms used in the SearchAPI project to secure sensitive data during transit and at rest. 

'encryption': {
  "secret_key": "/path/to/secret.key"
}

or

'encryption': {
  "secret_key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}

If no secret_key is specified, a new secret key will be automatically generated and saved in the config/auth folder.

Please note that the secret_key property can either be a file path (string with format "file-path") or a string value. Ensure that the secret key is secure and has a sufficient length for encryption purposes.


Inner Crypto Module (utils.crypto)

The utils.crypto module provides various encryption and cryptographic utility functions used within the SearchAPI project. These functions are designed to ensure the security and integrity of sensitive data. This document outlines the available functions and their usage.

Hashing Functions

hash_sha256

This function computes the SHA-256 hash of the input data and returns it as a hexadecimal code. The data parameter can be either a string or a byte array. The returned hash value is a string.

hash_sha1

This function computes the SHA-1 hash of the input data and returns it as a hexadecimal code. The data parameter can be either a string or a byte array. The returned hash value is a string.

Generating Keys and Initialization Vectors (IV)

generate_iv

This function generates a random Initialization Vector (IV) of 16 bytes. The IV is used for encryption and decryption processes and ensures the uniqueness and unpredictability of encrypted data.

generate_secret_key

This function generates a secret key of the specified size (default is 32 bytes). The secret key is used for encryption and decryption. The returned key is a base64-encoded string.

Encryption and Decryption

is_encrypted

This function checks whether the given text is encrypted. It returns True if the text has the expected encryption prefix ("encrypted:").

encrypt

This function encrypts the provided text using AES encryption. If an initialization vector (iv) is not provided, a random IV is generated. The encrypted text is returned with the prefix "encrypted:" and includes the IV and ciphertext.

decrypt

This function decrypts the given text using AES decryption. If the text is not encrypted (does not have the encryption prefix), it returns the original text. Otherwise, it extracts the IV and ciphertext from the encrypted text and performs decryption to obtain the original plaintext.

Usage

To use the functions from utils.crypto, you can import them as follows:

from utils.crypto import hash_sha256, hash_sha1, generate_iv, generate_secret_key, is_encrypted, encrypt, decrypt

You can then call the functions according to your requirements. For example:

data = "Hello, World!"

# Compute SHA256 hash
hash_value = hash_sha256(data)
print(f"SHA256 hash: {hash_value}")

# Generate a random IV
iv = generate_iv()
print(f"Generated IV: {iv}")

# Generate a secret key
secret_key = generate_secret_key()
print(f"Generated secret key: {secret_key}")

# Check if a text is encrypted
encrypted_text = encrypt(data)
is_text_encrypted = is_encrypted(encrypted_text)
print(f"Is the text encrypted? {is_text_encrypted}")

# Decrypt the encrypted text
decrypted_text = decrypt(encrypted_text)
print(f"Decrypted text: {decrypted_text}")

By utilizing the functions provided in utils.crypto, you can enhance the security and encryption capabilities within the SearchAPI project.


Accesing User Information

The user information is available in all the request, at any point of the code after the Security check, to access this information you can put req.state.user

from starlette.requests import Request
from typing import List
from gaia_core.models.security import User
 
router = APIRouter(prefix=f'/{os.path.basename(__file__)[:-3]}')


@router.get("/route")
async def route_handler(req: Request):

	# Get the user Roles
	user:User = req.state.user

The data mapped in the config of the security method will be available in the User object with the mapped field name, and any non mapped data will be available with the original field name



Roles

This configuration allows to add additional role for role verification using a CSV file. The file should contain the following headers:

  • ID: Unique ID for the role
  • Role: Display name of the role
  • Value: Numeric value assigned to the role for hierarchical comparisons

These roles are later loaded into the RoleManager, which by default has five roles defined: UNAUTHORIZED, ANONYMOUS, USER, ADMIN, READ_ONLY.

Ensure that the CSV file specified in the file property follows the correct format and contains the necessary role definitions.

Configuration

PropertyDescriptionTypeRequiredAvailable on version
fileCSV file with the definition of the roles. The file should have the following headers: ID, Role, Value.string/file-pathYes1.0+
role_equivalents_map

dict to map an existing role (provided by the application) to one or more equivalent role configured by the user

dict[string,list]Yes3.0+

Example Configuration

'roles': {
    'file': '/path/to/roles.csv'
    'role_equivalents_map': {
    	'unauthorized': 'no_auth',
        # 'anonymous':
        'user': 'write',
        'admin': ['super_user', 'administrator'],
        'read_only': ['read'],
        'root': ['adminIT']
    }
}   

Explanation for role equivalent:

The code checks if there are role equivalents defined in the configuration and processes each key-value pair, where the keys are primary roles and the values are either single or multiple equivalent roles. For each equivalent role, it uses the get_or_create method to ensure the role exists, assigning it the same name and value as the original role but with a different ID. This guarantees that all roles and their equivalents specified in the configuration are created and managed in the system, maintaining consistent attributes except for the unique ID.

Example Role CSV

ID,Role,Value
1,Admin,100
2,User,50
3,Guest,10

Roles Generated from Attribute Mapping 

For the roles mapped in the attributesMapping field from any of the authentication methods above, these are generated into the Roles Manager, using the name of the role as the ID, meaning you would be able to use this roles as any other default role, by using:

custom_role = roles_manager.get('Support')

The roles generate in this way will always have a value of 0,  unless they are specified in the Role CSV, in which case they will use the value defined there


Accessing the Roles of the User

To access the user roles, you first need to include the request in the endpoint, after that just access the state, then the user and finally the roles. We recomend to store the roles in a variable of type List[Role]

from starlette.requests import Request
from typing import List
from models.security import Role
outer = APIRouter(prefix=f'/{os.path.basename(__file__)[:-3]}')
@router.get("/route")
async def route_handler(req: Request):

# Get the user Roles
roles: List[Role] = req.state.user.roles

Role Manager 

The RolesManager class is responsible for managing roles within the SearchAPI project. It allows the creation, retrieval, and modification of roles.

Usage

To use the RolesManager, you can import anywhere in the code as follows:

from models.security import roles_manager

Creating Roles

The RolesManager class provides the get_or_create method to create and retrieve roles. You can create a new role or get an existing one using the get_or_create method.

# Create or get the "user" role
user_role = roles_manager.get_or_create(id="user", name="User", value=10)

# Create or get the "admin" role
admin_role = roles_manager.get_or_create(id="admin", name="Admin", value=100)

If a role with the specified id already exists, it will be retrieved. Otherwise, a new role will be created.

Retrieving Roles

To retrieve a role by its id, you can use the get_role method:

# Get the "user" role
user_role = roles_manager.get_role("user")

# Get the "admin" role
admin_role = roles_manager.get_role("admin")

If the specified role does not exist, a new role with the provided id will be created and returned.

Modifying Roles

The RolesManager class allows you to overwrite the properties of an existing role using the overwrite_role method:

# Create a new role
custom_role = Role(id="custom", name="Custom Role", value="custom_value")

# Overwrite the "user" role with the custom role
roles_manager.overwrite_role(custom_role)

or

# Create or get the "user" role
user_role = roles_manager.get_role("user")

# Overwrite the "user" role with the new values
admin_role = roles_manager.get_or_create(id="user", name="User", value=50, overwrite=True)

If the role with the specified id already exists, its name and value properties will be updated with the values from the custom_role object.

Predefined Roles

The RolesManager class also defines several predefined roles as class attributes:

  • UNAUTHORIZED: Represents an unauthorized role.
  • ANONYMOUS: Represents an anonymous role.
  • USER: Represents a user role.
  • ADMIN: Represents an admin role.
  • READ_ONLY: Represents a read-only role.
  • ROOT: Represents a role above application admins, like IT admins

These roles can be accessed directly from the RolesManager instance:

# Access the predefined roles
unauthorized_role = roles_manager.UNAUTHORIZED
anonymous_role = roles_manager.ANONYMOUS
user_role = roles_manager.USER
admin_role = roles_manager.ADMIN
read_only_role = roles_manager.READ_ONLY
root = roles_manager.ROOT

By utilizing the RolesManager class, you can easily create, retrieve, and modify roles within the SearchAPI project.

Role API Verification

The RoleVerifications class is a dependency class designed for FastAPI. It provides role-based authorization checks for user requests. This documentation describes the available methods and their usage.

Usage

To use the RoleVerifications class, you can create an instance of it with the desired roles:

role_verifications = RoleVerifications(roles=[roles_manager.ADMIN, roles_manager.READ_ONLY, roles_manager.get('Support')])

or

# Declared directly in the route with the verification type
@router.get('/route', dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.READ_ONLY, roles_manager.get('Support')]).requires_all()])

requires_any

The requires_any method allows access to users with any of the defined roles. 

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).requires_any()])
async def route_handler():     # Handle route logic

If the user has all of the specified roles, a 403 Forbidden HTTP exception will be raised.

requires_all

The requires_all method allows access to users who have all of the defined roles. 

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).requires_all()])
async def route_handler():
    # Handle route logic

If the user has all of the specified roles, a 403 Forbidden HTTP exception will be raised.

forbid_any

The forbid_any method forbids access to users with any of the defined roles.

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).forbid_any()])
async def route_handler():
     # Handle route logic

If the user has all of the specified roles, a 403 Forbidden HTTP exception will be raised.

forbid_all

The forbid_all method forbids access to users who have all of the defined roles.

app.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).forbid_all()])
async def route_handler():
     # Handle route logic

If the user has all of the specified roles, a 403 Forbidden HTTP exception will be raised.


higher_than

Since version 2.1

The higher_than method forbids access to users who have a role value lower or equals to the ones specified

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).higher_than()])
async def route_handler():
     # Handle route logic

Autogenerated Roles (i.e. Not default roles, will normally have a value of 0, if they are not specified in the Role CSV)

If the user doesn't have a role with higher value, a 403 Forbidden HTTP exception will be raised.

higher_equals

Since version 2.1

The higher_equals method forbids access to users who have  a role value lower to the ones specified

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).higher_equals()])
async def route_handler():
     # Handle route logic

Autogenerated Roles (i.e. Not default roles, will normally have a value of 0, if they are not specified in the Role CSV)

If the user doesn't have a role with higher or equal value, a 403 Forbidden HTTP exception will be raised.


equals

Since version 3.0

The equals method forbids access to users who have  a role value different to the ones specified

@router.get("/route", dependencies=[RoleVerifications([roles_manager.ADMIN, roles_manager.get('Support')]).equals()])
async def route_handler():
     # Handle route logic

Autogenerated Roles (i.e. Not default roles, will normally have a value of 0, if they are not specified in the Role CSV)

If the user doesn't have a role with higher or equal value, a 403 Forbidden HTTP exception will be raised.


By utilizing the methods provided by the role verification, you can enforce role-based authorization checks in your FastAPI routes, ensuring that only users with the required roles can access the protected endpoints.


Headers

This configuration allows to add additional headers on GAIA API's responses

The headers added in this way are going to be added to all of the responses, hence it's recommended for security headers like the Strict Transport Security headers and others of such nature.

Configuration

PropertyDescriptionTypeRequired
includeList of tuples representing header name and its value.list[Tuple[str, str]]False

Example Configuration

'headers': {
    'include': [
        ('strict-transport-security', 'max-age=31536000;includeSubDomains')
    ]
}