Construire un BBQ connecté serverless en tant que SaaS - Partie 4 - AuthZ

Ce fichier a ete traduit automatiquement par IA, des erreurs peuvent survenir
Nous avons déjà abordé l'isolation des données et la sécurité au niveau des lignes dans un environnement SaaS multi-tenant. Il est maintenant temps d'examiner comment gérer l'autorisation, le processus de contrôle d'accès aux données et aux fonctionnalités en fonction de l'identité de l'utilisateur et de ce qu'il est autorisé à faire.
Dans cet article, nous explorerons l'architecture d'un Point de Décision d'Autorisation (PDP) centralisé avec des Points de Mise en Œuvre d'Autorisation (PEPs) distribués pour garantir une autorisation cohérente et sécurisée sur votre plateforme SaaS. Nous détaillerons également les différences clés entre l'Authentification (AuthN) et l'Autorisation (AuthZ).
Si vous ne l'avez pas encore consulté, voici la première partie, la deuxième partie et la troisième partie
Authentification (AuthN) vs. Autorisation (AuthZ)
Avant de commencer à construire l'architecture, nous devons établir une compréhension commune. Il est crucial de faire la distinction entre l'Authentification et l'Autorisation, deux termes souvent confondus, et que j'ai dû expliquer à de nombreuses reprises, mais qui servent des objectifs très différents.
Authentification (AuthN)
L'authentification consiste à vérifier l'identité. Elle répond à la question, Qui êtes-vous ? Lorsqu'un utilisateur se connecte à notre application SaaS, l'authentification garantit qu'il est bien celui qu'il prétend être. Cela peut impliquer quelque chose d'aussi simple qu'un nom d'utilisateur et un mot de passe ou une authentification à facteur multiple (MFA) plus complexe.
Nous utilisons Amazon Cognito User Pools pour notre authentification dans cette série.
Autorisation (AuthZ)
Une fois l'identité de l'utilisateur authentifiée, l'autorisation entre en jeu. Ce processus répond à la question, Que pouvez-vous faire ? L'autorisation détermine les ressources, données et actions auxquelles un utilisateur est autorisé à accéder en fonction de ses rôles et permissions.
Dans un environnement multi-tenant, il est important de bien gérer l'autorisation. Nous devons nous assurer que les utilisateurs ne peuvent accéder qu'aux données et fonctionnalités pertinentes à leur locataire et à son rôle. C'est là qu'un système d'autorisation robuste et évolutif entre en jeu.
PDP centralisé et PEPs distribués : La colonne vertébrale de l'autorisation
Maintenant que nous avons clarifié la différence entre AuthN et AuthZ, concentrons-nous sur la construction d'un système d'autorisation utilisant un PDP centralisé et des PEPs distribués.
PDP centralisé
Un Point de Décision d'Autorisation (PDP) est l'endroit où toutes vos décisions d'autorisation sont prises. C'est le cerveau du système d'autorisation, évaluant chaque demande par rapport à des politiques ou rôles prédéfinis et déterminant si la demande doit être autorisée ou refusée. Il existe quelques avantages clairs avec un PDP centralisé :
Cohérence : Chaque demande est évaluée par rapport au même ensemble de politiques, garantissant une prise de décision cohérente à travers nos systèmes.
Gestion et conformité : Avec toutes les politiques gérées en un seul endroit, les mises à jour et les audits sont facilités. La journalisation des décisions à des fins d'audit est importante pour certaines conformités réglementaires.
Cependant, certains inconvénients seraient l'augmentation de la latence. Le routage de chaque demande d'autorisation via un seul PDP peut ralentir les choses, surtout dans un système distribué.
En utilisant Amazon Verified Permissions (AVP), un PDP centralisé est requis.
PEPs distribués
Les Points de Mise en Œuvre d'Autorisation (PEPs) sont l'endroit où les décisions d'autorisation prises par le PDP sont appliquées. Ces points sont distribués dans notre système. Notre Authorizer basé sur Lambda est un exemple de PEP. La porte d'entrée de nos microservices peut également agir comme PEP et appeler notre PDP.
Nous avons certains avantages à exécuter nos PEPs de manière distribuée.
Réduction de la latence : En plaçant les PEPs près de l'endroit où les décisions doivent être appliquées, nous pouvons réduire la latence, avec une stratégie de mise en cache cela peut être encore réduit.
Extensibilité : À mesure que votre système grandit, les PEPs distribués garantissent qu'aucun point unique ne devient un goulot d'étranglement, surtout avec le mise en cache activée.
Aperçu de l'architecture
Dans notre nouvelle architecture d'autorisation, nous allons introduire un nouveau service d'autorisation, qui sera notre PDP central. La logique d'autorisation sera déplacée de notre Authorizer Lambda précédent vers le PDP, au lieu de cela, notre Authorizer Lambda appellera maintenant le PDP qui renverra une décision d'accès.

Créer le service d'autorisation (PDP)
La première chose que nous devons faire est de créer notre Service d'Autorisation (PDP), comme indiqué, ce sera un API Gateway et une fonction Lambda. Une approche intéressante que nous pourrions utiliser est de supprimer le API Gateway et d'utiliser simplement les URL de fonction Lambda. Cependant, pour une plus grande extensibilité plus tard, j'ai décidé d'utiliser un API Gateway.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Connected BBQ Application Tenant Service
Parameters:
ApplicationName:
Type: String
Description: Name of owning application
Default: bbq-iot
UserPoolStackName:
Type: String
Description: The name of the Stack with the Cognito User Pool
Globals:
Function:
Timeout: 30
MemorySize: 2048
Runtime: python3.12
Resources:
LambdaPDPFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/AuthZ
Handler: authz.handler
Environment:
Variables:
JWKS_URL:
Fn::ImportValue: !Sub ${UserPoolStackName}:jwks-url
AUDIENCE:
Fn::ImportValue: !Sub ${UserPoolStackName}:app-audience
Events:
AuthZ:
Type: Api
Properties:
Path: /authz
Method: post
RestApiId: !Ref PDPApi
Auth:
AuthorizationType: AWS_IAM
PDPApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub ${ApplicationName}-pdp-api
StageName: prod
EndpointConfiguration: REGIONAL
Cors:
AllowMethods: "'GET,PUT,POST,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
DefaultAuthorizer: AWS_IAM
Outputs:
PDPApiID:
Value: !Ref PDPApi
Description: The ID of the PDP API
Export:
Name: !Sub ${AWS::StackName}:pdp-api-id
Notre API utilisera AWS IAM pour l'autorisation machine-à-machine, tout comme notre API d'administration que nous avons créée dans notre partie précédente.
Ensuite, nous déplaçons notre logique AuthZ de notre Authorizer Lambda vers notre nouvelle implémentation PDP. Nous gérons à la fois l'autorisation pour l'accès utilisateur et locataire et introduisons deux nouvelles ressources tenant et tenantUser.
import os
import json
import jwt
from jwt import PyJWKClient
def handler(event, context):
data = json.loads(event["body"])
jwt_token = data["jwt_token"]
try:
jwks_url = os.environ["JWKS_URL"]
jwks_client = PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(jwt_token)
decoded_token = jwt.decode(
jwt_token,
signing_key.key,
algorithms=["RS256"],
audience=os.environ["AUDIENCE"],
)
# Vérifier la ressource et prendre une décision d'AuthZ
if data["resource"] == "tenant":
tenant_id = data["tenant_id"]
token_tenant_id = decoded_token.get("tenant")
if token_tenant_id == tenant_id:
response_body = generate_access(
"Allow", data["action"], data["resource"]
)
return {
"statusCode": 200,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
elif data["resource"] == "tenantUser":
user_id = data["user_id"]
token_user_id = decoded_token.get("cognito:username")
if token_user_id == user_id:
response_body = generate_access(
"Allow", data["action"], data["resource"]
)
return {
"statusCode": 200,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
except Exception as e:
print(f"Authorization error: {str(e)}")
# Générer une réponse par défaut qui refuse l'accès
response_body = generate_access("Deny", data["action"], data["resource"])
return {
"statusCode": 403,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
def generate_access(effect, action, resource):
auth_response = {
"effect": effect,
"action": action,
"resource": resource,
}
return auth_response
Ensuite, nous devons bien sûr mettre à jour l'Authorizer Lambda pour appeler notre PDP pour l'autorisation.
api_endpoint = os.environ.get("PDP_AUTHZ_API_ENDPOINT")
def handler(event, context):
token = event["headers"].get("authorization", "")
if not token:
raise Exception("Unauthorized")
token = token.replace("Bearer ", "")
try:
......
path_tenant_id = event["pathParameters"]["tenantId"]
session = boto3.Session()
credentials = session.get_credentials().get_frozen_credentials()
region = os.environ["AWS_REGION"]
auth = AWS4Auth(
credentials.access_key,
credentials.secret_key,
region,
"execute-api",
session_token=credentials.token,
)
data = {
"jwt_token": token,
"resource": "tenant",
"action": "read",
"resource_path": event["path"],
"tenant_id": event["pathParameters"]["tenantId"],
}
headers = {"Content-type": "application/json"}
response = requests.post(
api_endpoint + "/authz", data=json.dumps(data), headers=headers, auth=auth
)
......
except Exception as e:
print(f"Authorization error: {str(e)}")Résumé
Avec ces modifications, nous avons déplacé notre autorisation vers un PDP centralisé avec une configuration PEP distribuée. Cela crée une bonne base pour continuer à faire évoluer notre AuthZ. Dans les articles futurs, nous introduirons un RBAC (Role Based Access Control) et un ABAC (Attribute Based Access Control), nous passerons également à utiliser Amazon Verified permissions.
Obtenir le code
La configuration complète avec tout le code est disponible sur Serverless Handbook
Dernières paroles
Dans cet article, nous avons examiné l'architecture d'un système d'autorisation centralisé utilisant un PDP et des PEPs distribués. Nous avons également mis en lumière les différences entre l'Authentification (AuthN) et l'Autorisation (AuthZ).
Dans les prochains articles, nous commencerons à examiner l'onboarding des appareils et les données, restez à l'écoute !
Découvrez Mon manuel serverless pour le code de la solution construite dans cette série d'articles.
N'oubliez pas de me suivre sur LinkedIn et X pour plus de contenu, et lisez le reste de mes Blogs
Comme le dit Werner ! Maintenant, allez construire !