@@ -139,12 +151,7 @@ function Course({ id, name, access_period, cert, draft }: Course) {
)}
-
- {name}{' '}
- {draft ? (
- (rascunho)
- ) : null}
-
+ {name}
@@ -180,6 +187,12 @@ function Course({ id, name, access_period, cert, draft }: Course) {
)}
+
+ {draft && (
+
+
+
+ )}
diff --git a/id.saladeaula.digital/app/app.py b/id.saladeaula.digital/app/app.py
index 6da7259..c23eb3d 100644
--- a/id.saladeaula.digital/app/app.py
+++ b/id.saladeaula.digital/app/app.py
@@ -7,24 +7,26 @@ from aws_lambda_powertools.event_handler.api_gateway import (
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext
+from routes.authentication import router as authentication
from routes.authorize import router as authorize
from routes.jwks import router as jwks
from routes.openid_configuration import router as openid_configuration
+from routes.register import router as register
from routes.revoke import router as revoke
-from routes.session import router as session
from routes.token import router as token
from routes.userinfo import router as userinfo
logger = Logger(__name__)
tracer = Tracer()
app = APIGatewayHttpResolver(enable_validation=True)
-app.include_router(session)
+app.include_router(authentication)
app.include_router(authorize)
app.include_router(jwks)
+app.include_router(openid_configuration)
+app.include_router(register)
+app.include_router(revoke)
app.include_router(token)
app.include_router(userinfo)
-app.include_router(revoke)
-app.include_router(openid_configuration)
@app.get('/health')
diff --git a/id.saladeaula.digital/app/oauth2.py b/id.saladeaula.digital/app/oauth2.py
index aaa6604..25e97b8 100644
--- a/id.saladeaula.digital/app/oauth2.py
+++ b/id.saladeaula.digital/app/oauth2.py
@@ -198,23 +198,6 @@ class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
)
-class TokenExchangeGrant(grants.BaseGrant):
- GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange'
-
- TOKEN_ENDPOINT_AUTH_METHODS = [
- 'client_secret_basic',
- 'client_secret_post',
- ]
-
- @hooked
- def validate_token_request(self):
- raise NotImplementedError()
-
- @hooked
- def create_token_response(self):
- raise NotImplementedError()
-
-
class RefreshTokenGrant(grants.RefreshTokenGrant):
TOKEN_ENDPOINT_AUTH_METHODS = [
'client_secret_basic',
@@ -280,6 +263,23 @@ class RefreshTokenGrant(grants.RefreshTokenGrant):
)
+class TokenExchangeGrant(grants.BaseGrant):
+ GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange'
+
+ TOKEN_ENDPOINT_AUTH_METHODS = [
+ 'client_secret_basic',
+ 'client_secret_post',
+ ]
+
+ @hooked
+ def validate_token_request(self):
+ raise NotImplementedError()
+
+ @hooked
+ def create_token_response(self):
+ raise NotImplementedError()
+
+
class RevocationEndpoint(rfc7009.RevocationEndpoint):
def query_token( # type: ignore
self,
diff --git a/id.saladeaula.digital/app/routes/authorize.py b/id.saladeaula.digital/app/routes/authorize.py
index c913109..7df328c 100644
--- a/id.saladeaula.digital/app/routes/authorize.py
+++ b/id.saladeaula.digital/app/routes/authorize.py
@@ -24,7 +24,7 @@ dyn = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client)
def authorize():
current_event = router.current_event
cookies = parse_cookies(current_event.get('cookies', []))
- session = cookies.get('__session')
+ session = cookies.get('SID')
if not session:
raise BadRequestError('Missing session')
diff --git a/id.saladeaula.digital/app/routes/session.py b/id.saladeaula.digital/app/routes/session.py
deleted file mode 100644
index 1aa6011..0000000
--- a/id.saladeaula.digital/app/routes/session.py
+++ /dev/null
@@ -1,158 +0,0 @@
-from http import HTTPStatus
-from typing import Annotated
-from uuid import uuid4
-
-import boto3
-from aws_lambda_powertools.event_handler import (
- Response,
-)
-from aws_lambda_powertools.event_handler.api_gateway import Router
-from aws_lambda_powertools.event_handler.exceptions import ForbiddenError, NotFoundError
-from aws_lambda_powertools.event_handler.openapi.params import Body
-from aws_lambda_powertools.shared.cookies import Cookie
-from layercake.dateutils import now, ttl
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
-from passlib.hash import pbkdf2_sha256
-
-from boto3clients import dynamodb_client
-from config import (
- OAUTH2_TABLE,
- SESSION_EXPIRES_IN,
-)
-
-router = Router()
-dyn = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client)
-idp = boto3.client('cognito-idp')
-
-
-@router.post('/session')
-def session(
- username: Annotated[str, Body()],
- password: Annotated[str, Body()],
-):
- user_id, password_hash = _get_user(username)
-
- if not password_hash:
- _get_idp_user(user_id, username, password)
- else:
- if not pbkdf2_sha256.verify(password, password_hash):
- raise ForbiddenError('Invalid credentials')
-
- return Response(
- status_code=HTTPStatus.OK,
- cookies=[
- Cookie(
- name='__session',
- value=new_session(user_id),
- http_only=True,
- secure=True,
- same_site=None,
- max_age=SESSION_EXPIRES_IN,
- )
- ],
- )
-
-
-def _get_user(username: str) -> tuple[str, str | None]:
- sk = SortKey(username, path_spec='user_id')
- user = dyn.collection.get_items(
- KeyPair(pk='email', sk=sk, rename_key=sk.path_spec)
- + KeyPair(pk='cpf', sk=sk, rename_key=sk.path_spec),
- )
-
- if not user:
- raise UserNotFoundError()
-
- password = dyn.collection.get_item(
- KeyPair(
- pk=user['user_id'],
- sk=SortKey(
- sk='PASSWORD',
- path_spec='hash',
- rename_key='password',
- ),
- ),
- raise_on_error=False,
- default=None,
- # Uncomment the following line when removing support for Cognito
- # exc_cls=UserNotFoundError,
- )
-
- return user['user_id'], password
-
-
-def _get_idp_user(
- user_id: str,
- username: str,
- password: str,
-) -> bool:
- import base64
- import hashlib
- import hmac
-
- # That should be removed when completing the migration
- # to our own OAuth2 implementation.
- client_id = '3ijacqc7r2jc9l4oli2b41f7te'
- client_secret = 'amktf9l40g1mlqdo9fjlcfvpn2cp3mvh4pt97hu55sfelccos58'
-
- dig = hmac.new(
- client_secret.encode('utf-8'),
- msg=(username + client_id).encode('utf-8'),
- digestmod=hashlib.sha256,
- ).digest()
-
- try:
- idp.initiate_auth(
- AuthFlow='USER_PASSWORD_AUTH',
- AuthParameters={
- 'USERNAME': username,
- 'PASSWORD': password,
- 'SECRET_HASH': base64.b64encode(dig).decode(),
- },
- ClientId=client_id,
- )
-
- dyn.put_item(
- item={
- 'id': user_id,
- 'sk': 'PASSWORD',
- 'hash': pbkdf2_sha256.hash(password),
- 'created_at': now(),
- }
- )
- except Exception:
- raise ForbiddenError('Invalid credentials')
-
- return True
-
-
-def new_session(sub: str) -> str:
- sid = str(uuid4())
- now_ = now()
- exp = ttl(start_dt=now_, seconds=SESSION_EXPIRES_IN)
-
- with dyn.transact_writer() as transact:
- transact.put(
- item={
- 'id': 'SESSION',
- 'sk': sid,
- 'user_id': sub,
- 'ttl': exp,
- 'created_at': now_,
- }
- )
- transact.put(
- item={
- 'id': sub,
- 'sk': f'SESSION#{sid}',
- 'ttl': exp,
- 'created_at': now_,
- }
- )
-
- return f'{sid}:{sub}'
-
-
-class UserNotFoundError(NotFoundError):
- def __init__(self, *_):
- super().__init__('User not found')
diff --git a/id.saladeaula.digital/template.yaml b/id.saladeaula.digital/template.yaml
index 4fb57e5..f6520d6 100644
--- a/id.saladeaula.digital/template.yaml
+++ b/id.saladeaula.digital/template.yaml
@@ -56,10 +56,16 @@ Resources:
- cognito-idp:InitiateAuth
Resource: !Sub arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*
Events:
- Session:
+ Authentication:
Type: HttpApi
Properties:
- Path: /session
+ Path: /authentication
+ Method: POST
+ ApiId: !Ref HttpApi
+ Register:
+ Type: HttpApi
+ Properties:
+ Path: /register
Method: POST
ApiId: !Ref HttpApi
Authorize:
diff --git a/packages/ui/src/components/data-table/data-table.tsx b/packages/ui/src/components/data-table/data-table.tsx
index bcf9020..0d18579 100644
--- a/packages/ui/src/components/data-table/data-table.tsx
+++ b/packages/ui/src/components/data-table/data-table.tsx
@@ -271,7 +271,10 @@ export function DataTable
({
{row.getVisibleCells().map((cell) => {
const isPinned = cell.column.getIsPinned()
@@ -281,8 +284,13 @@ export function DataTable({
key={cell.id}
className={cn(
isPinned &&
- 'lg:sticky z-1 bg-card group-hover:bg-muted group-has-data-[state=open]:bg-muted',
- isPinned && isSelected && 'bg-muted',
+ 'lg:sticky z-1 bg-card group-has-data-[state=open]:bg-muted',
+ isPinned &&
+ !isSelected &&
+ 'group-hover:bg-muted',
+ isPinned &&
+ isSelected &&
+ 'bg-gray-50 dark:bg-neutral-900',
isPinned === 'left' && 'left-0',
isPinned === 'right' && 'right-0',
// Override the shadcn class
diff --git a/packages/ui/src/routes/users/data.tsx b/packages/ui/src/routes/users/data.tsx
index 45ca593..487f0c4 100644
--- a/packages/ui/src/routes/users/data.tsx
+++ b/packages/ui/src/routes/users/data.tsx
@@ -4,8 +4,7 @@ export type User = {
id: string
name: string
email: string
- cpf?: string
- cnpj?: string
+ cpf: string
}
export const headers = {