Skip to content

Commit

Permalink
adicionando configuração do keycloak
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Ferino committed Apr 14, 2024
1 parent 69bc429 commit cb0dc1a
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 16 deletions.
4 changes: 4 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_CREATE=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache

RUN pip install --upgrade pip && apk add -u build-base gcc musl-dev libffi-dev openssl-dev cargo

COPY pyproject.toml ./
RUN pip install poetry==1.1.12

Expand All @@ -20,6 +22,8 @@ COPY --from=builder /opt/app/.venv /opt/app/.venv

ENV PATH="/opt/app/.venv/bin:$PATH"

RUN apk --no-cache add musl-dev libgcc

WORKDIR /opt/app

COPY api ./api
Expand Down
2 changes: 1 addition & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ run: build
@echo
@echo "******************** RUNNING DOCKER INSTANCE **********************************"
docker build -t ${FLASK_APP_DOCKER}:${DOCKER_IMAGE_VERSION} .
docker run --detach --rm -p ${DOCKER_HOST_PORT}:${DOCKER_INTERNAL_PORT} --name ${CONTAINER_NAME} ${FLASK_APP_DOCKER}:${DOCKER_IMAGE_VERSION}
docker run --net=host --detach --rm -p ${DOCKER_HOST_PORT}:${DOCKER_INTERNAL_PORT} --name ${CONTAINER_NAME} ${FLASK_APP_DOCKER}:${DOCKER_IMAGE_VERSION}
@echo
@echo
docker ps
Expand Down
5 changes: 4 additions & 1 deletion app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Trata-se de uma aplicação Flask - API REST. Através dela os internautas enviam comentários em texto de uma máteria e acompanham o que outras pessoas estão falando sobre o assunto em destaque. O funcionamento básico da API consiste em uma rota para inserção dos comentários e uma rota para listagem.

É utilizado o Poetry para gerenciamento de ambiente virtual (virtualenv) e gerenciamento de pacotes. A lista de pacotes está disponível em `pyproject`. Caso deseje adicionar um novo pacotes execute `poetry add <nome do pacote>`.

## Requisitos

- Docker (_versão 25.0.4_)
Expand Down Expand Up @@ -155,4 +157,5 @@ $ sudo apt install gunicorn
$ gunicorn --log-level debug api:app
```
A aplicação está disponível em [http://localhost:8000](http://localhost:8000).
A aplicação estará disponível em [http://localhost:8000](http://localhost:8000).
10 changes: 5 additions & 5 deletions app/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from config import LOGSTASH_HOST, LOGSTASH_PORT

logstash_handler = logging.handlers.SocketHandler(LOGSTASH_HOST, LOGSTASH_PORT)
logstash_handler.setFormatter(LogstashFormatterV1())
logger = logging.getLogger()
logger.addHandler(logstash_handler)
logger.setLevel(logging.DEBUG)
# logstash_handler = logging.handlers.SocketHandler(LOGSTASH_HOST, LOGSTASH_PORT)
# logstash_handler.setFormatter(LogstashFormatterV1())
# logger = logging.getLogger()
# logger.addHandler(logstash_handler)
# logger.setLevel(logging.DEBUG)
17 changes: 16 additions & 1 deletion app/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
from os import urandom

FLASK_SECRET_KEY = urandom(24)

LOGSTASH_HOST='localhost'
LOGSTASH_PORT=5000
LOGGING_FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOGGING_FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s'

KEYCLOAK_BASE_URL='http://localhost:8080'
KEYCLOAK_REALM='desafio-devops'
KEYCLOAK_CLIENT_ID='app'
KEYCLOAK_CLIENT_SECRET='iF0lbnfrexgY6Z2xvg4U4uT2ngteFVWO'
KEYCLOAK_REDIRECT_URI='http://localhost:8000/callback'
KEYCLOAK_AUTHORIZATION_URL=f'{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/auth'
KEYCLOAK_TOKEN_URL=f'{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/token'
KEYCLOAK_USERINFO_URL=f'{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/userinfo'
KEYCLOAK_JWKS_URI=f'{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/certs'
KEYCLOAK_LOGOUT_URL=f'{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/logout'
104 changes: 96 additions & 8 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,106 @@
from flask import Flask
from flask import jsonify
from flask import request
from flask import Flask, jsonify, request, session, redirect, url_for
from functools import wraps
from requests import get

from authlib.integrations.flask_client import OAuth
from http import HTTPStatus
from flask_wtf.csrf import CSRFProtect, generate_csrf

from api.utils import logger


from config import KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_AUTHORIZATION_URL
from config import KEYCLOAK_TOKEN_URL, KEYCLOAK_USERINFO_URL, KEYCLOAK_REDIRECT_URI
from config import FLASK_SECRET_KEY, KEYCLOAK_JWKS_URI, KEYCLOAK_LOGOUT_URL


# from api.utils import logger

app_name = 'comentarios'
app = Flask(app_name)
app.secret_key= FLASK_SECRET_KEY
app.debug = True

comments = {}
csrf = CSRFProtect(app)


comments: dict = {}


oauth = OAuth(app)
oauth.register(
name='keycloak',
client_id=KEYCLOAK_CLIENT_ID,
client_secret=KEYCLOAK_CLIENT_SECRET,
authorize_url=KEYCLOAK_AUTHORIZATION_URL,
authorize_params={'scope': 'openid profile email', 'nonce': 'your-nonce-value'},
access_token_url=KEYCLOAK_TOKEN_URL,
userinfo_endpoint=KEYCLOAK_USERINFO_URL,
jwks_uri=KEYCLOAK_JWKS_URI,
client_kwargs={'scope':'openid profile email'}
)

def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_info' not in session:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function


@app.route('/')
@login_required
def index():
app.logger.info('Calling index function')
user_info = session.get('user_info')

if user_info:
return user_info




@app.route('/login')
def login():
app.logger.info('Calling login function')
csrf_token = generate_csrf()
app.logger.debug('csrf_token: %s', csrf_token)
session['csrf_token'] = csrf_token
return oauth.keycloak.authorize_redirect(redirect_uri=KEYCLOAK_REDIRECT_URI, state=csrf_token)

@app.route('/logout')
def logout():
app.logger.info('Calling logout function')
session.pop('user_info', None)

logout_response = get(KEYCLOAK_LOGOUT_URL)
return redirect(logout_response.url)


@app.route('/callback')
def callback():
app.logger.info('Calling callback function')
csrf_token = session.pop('csrf_token', None)
app.logger.debug('csrf_token: %s', csrf_token)

state = request.args.get('state')
app.logger.debug('state: %s', state)

if csrf_token != state:
return {'message': 'Invalid CSRF Token' }, HTTPStatus.FORBIDDEN

token = oauth.keycloak.authorize_access_token()
app.logger.debug('Access Token: %s', token)
nonce = session.pop('nonce', None)
user_info = oauth.keycloak.parse_id_token(token, nonce=nonce)
app.logger.debug('User info: %s', user_info)

session['user_info'] = user_info
return redirect(url_for('index'))


@app.route('/api/comment/new', methods=['POST'])
@login_required
def api_comment_new():
request_data = request.get_json()

Expand All @@ -24,7 +111,7 @@ def api_comment_new():
new_comment = {
'email': email,
'comment': comment,
}
}

if content_id in comments:
comments[content_id].append(new_comment)
Expand All @@ -40,15 +127,16 @@ def api_comment_new():


@app.route('/api/comment/list/<content_id>')
@login_required
def api_comment_list(content_id):
content_id = '{}'.format(content_id)
logger.info('Trying to retrieve comment')
app.logger.info('Trying to retrieve comment')


if content_id in comments:
return jsonify(comments[content_id])
else:
logger.error('Comment id %s does not exist', content_id)
app.logger.error('Comment id %s does not exist', content_id)
message = 'content_id {} not found'.format(content_id)
response = {
'status': HTTPStatus.NOT_FOUND,
Expand Down
4 changes: 4 additions & 0 deletions app/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Jinja2 = "2.11.2"
MarkupSafe = "1.1.1"
Werkzeug = "1.0.1"
logstash_formatter = "0.5.17"
Authlib = "^1.3.0"
pyOpenSSL = "^24.1.0"
requests = "^2.31.0"
Flask-WTF = "^1.2.1"

[tool.poetry.dev-dependencies]

Expand Down
26 changes: 26 additions & 0 deletions local_infra/keycloak/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CONTAINER_NAME=keycloak_server
APP_DOCKER=quay.io/keycloak/keycloak
DOCKER_IMAGE_VERSION=24.0.2
DOCKER_HOST_PORT=8080
DOCKER_INTERNAL_PORT=8080

all: run
.PHONY: all run clean

run: clean
@echo
@echo "******************** RUNNING DOCKER INSTANCE **********************************"
docker run --net=host --detach --rm --name ${CONTAINER_NAME} -p ${DOCKER_HOST_PORT}:${DOCKER_INTERNAL_PORT} -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.2 start-dev
@echo
@echo
docker ps
@echo


clean:
@echo
@echo "******************** KILLING CONTAINER **********************************"
docker rm -f ${CONTAINER_NAME}
@echo
docker ps
@echo
2 changes: 2 additions & 0 deletions local_infra/keycloak/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.2 start-dev

docker run --detach --rm -p ${DOCKER_HOST_PORT}:${DOCKER_INTERNAL_PORT} -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin --name ${CONTAINER_NAME} ${APP_DOCKER}:${DOCKER_IMAGE_VERSION}

0 comments on commit cb0dc1a

Please sign in to comment.