Skip to content

Commit

Permalink
Fake oauth server (#996)
Browse files Browse the repository at this point in the history
* fake-oauth server

Simple server faking google oauth.

Changes in backend-project to be implemented in the next commit.

* FakeProvider talking to fake-oauth

* Configure oauth provider with a flag.

Added a `SOCIAL_AUTH_USE_FAKE_OAUTH` flag that, if set to true, will
configure the backend to use a fake oauth server.
Talks to a real google oauth api otherwise.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Lint fixes

* Mention fake-oauth in the main README.

* Disable fake_oauth for tests.

* Mention issues with old sessions in README

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
rwakulszowa and pre-commit-ci[bot] authored Jul 24, 2021
1 parent 1cd57b7 commit ad94772
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ build:
test: wait_mysql wait_minio test_backend test_openapi_spec

test_backend:
docker-compose run backend coverage run manage.py test --keepdb --parallel $(nproc) --verbosity=2 ${TEST}
docker-compose run -e SOCIAL_AUTH_USE_FAKE_OAUTH=False backend coverage run manage.py test --keepdb --parallel $(nproc) --verbosity=2 ${TEST}

coverage_html_backend:
docker-compose run backend coverage html
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ W celu uruchomienia projektu należy wykonać:
docker-compose up
```

Po pomyślnym uruchomieniu projektu środowisko pod adresem [http://localhost:8000/admin/](http://localhost:8000/admin/) winno być możliwe logowanie z wykorzystaniem loginu i hasła, a wszelkie zmiany kodu aplikacji w lokalnym repozytorium będą automatycznie załadowane przez Django.
Po pomyślnym uruchomieniu projektu środowisko pod adresem [http://localhost:8000/](http://localhost:8000/) winno być możliwe korzystanie z aplikacji
poprzez konto testowego, automatycznie stworzonego użytkownika. Kwestia autoryzacji opisana jest szerzej w folderze fake-oauth/.
Wszelkie zmiany kodu aplikacji w lokalnym repozytorium będą automatycznie załadowane przez Django.

W celu utworzenia konta administratora należy wykonać:

Expand Down
1 change: 1 addition & 0 deletions backend-project/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
]
SOCIAL_AUTH_USE_FAKE_OAUTH = env("SOCIAL_AUTH_USE_FAKE_OAUTH", default=False)

MINIO_ACCESS_KEY = env("MINIO_ACCESS_KEY")
MINIO_SECRET_KEY = env("MINIO_SECRET_KEY")
Expand Down
40 changes: 40 additions & 0 deletions backend-project/small_eod/users/providers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from requests_oauthlib import OAuth2Session


Expand Down Expand Up @@ -31,3 +33,41 @@ def exchange(self, request):
)
resp = google.get(self.userinfo_url)
return resp.json()


class FakeProvider:
base_url = "https://localhost:5678"

def __init__(self, *args, **kwargs):
pass

def callback_url(self, request):
# Hardcode localhost - the provider is expected to be used only in local
# deployments.
# `build_absolute_uri` doesn't work, because it produces a docker
# friendly url.
redirect_uri = "http://localhost:8000/login/callback"
return f"{self.base_url}?redirect_uri={redirect_uri}", None

def exchange(self, request):
# Hardcoded values.
# Simple, but working.
return {
"email": "[email protected]",
"given_name": "GivenName",
"family_name": "FamilyName",
}


def get_provider_cls():
flag_value = settings.SOCIAL_AUTH_USE_FAKE_OAUTH
if flag_value is True:
if not settings.DEBUG:
raise ImproperlyConfigured("Fake oauth may only be used in DEBUG mode")
return FakeProvider
elif flag_value is False:
return GoogleProvider
else:
raise ImproperlyConfigured(
f"Fake oauth must be either True or False, is {flag_value}"
)
4 changes: 2 additions & 2 deletions backend-project/small_eod/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from rest_framework_simplejwt.tokens import RefreshToken

from .filterset import UserFilterSet
from .providers import GoogleProvider
from .providers import get_provider_cls
from .serializers import (
RefreshTokenRequestSerializer,
RequestSerializer,
Expand All @@ -27,7 +27,7 @@ class UserViewSet(viewsets.ModelViewSet):

queryset = User.objects.all()
serializer_class = UserSerializer
provider = GoogleProvider(
provider = get_provider_cls()(
client_id=settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY,
client_secret=settings.SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET,
scopes=settings.SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE,
Expand Down
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ services:
target: dev
depends_on:
- db
- fake-oauth
volumes:
- ./backend-project:/code
- static:/code/static
Expand All @@ -47,6 +48,8 @@ services:
MINIO_URL: http://minio:9000
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: "x-x.apps.googleusercontent.com"
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: "x-puMp99EjG7"
# Fake oauth - set to True only for local development
SOCIAL_AUTH_USE_FAKE_OAUTH: "True"
# SECRET_KEY
# DJANGO_ALLOWED_HOSTS
expose:
Expand Down Expand Up @@ -79,6 +82,15 @@ services:
MINIO_ACCESS_KEY: test
MINIO_SECRET_KEY: test8chars

# fake oauth server for local development *only*.
fake-oauth:
build:
context: ./fake-oauth
ports:
- "5678:5678"
expose:
- "5678"

volumes:
static:
media:
15 changes: 15 additions & 0 deletions fake-oauth/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:latest

WORKDIR oauth

# Generate a self signed certificate. This will let us handle https requests.
RUN openssl genrsa -out key.pem
RUN openssl req -new -key key.pem -out csr.pem -batch
RUN openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
RUN rm csr.pem

COPY server.js ./

EXPOSE 5678

CMD ["node", "server.js"]
21 changes: 21 additions & 0 deletions fake-oauth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# small_eod fake-oauth

Zawiera prosty serwer umożliwiający lokalne testowanie aplikacji, bez konieczności komunikacji z serwerem oauth.

Tylko i wyłącznie do lokalnego testowania.

## Uruchamianie

Serwer uruchomi się razem z pozostałymi komponentami przy użyciu docker-compose.
Uwaga - aby aplikacja korzystała z fake-oauth zamiast prawdziwego serwera oauth, konieczne są zmiany w konfiguracji backend-project. Więcej informacji można znaleźć w folderze projektu backendowego.
Zaimplementowano jedynie absolutne minimum funkcjonalności.

## Ostrzeżenie przeglądarki

Większość przeglądarek wyświetli ostrzeżenie przy pierwszym kontakcie z serwerem, spododowane samodzielnie podpisanym certyfikatem.
O ile w przypadku publicznych serwerów jest to oznaka potencjalnego zagrożenia, w tym przypadku ostrzeżenie można zignorować.

## Pętla przekierowań

Jeśli przeglądarka ma zapisane w pamięci cookie po zalogowaniu do serwisu (powstałe, na przykład, po zalogowaniu poprzed panel admin/), próba logowania
może się nie powieść. Rozwiązaniem jest usunięcie cookie lub użycie karty prywatnej do testowania.
36 changes: 36 additions & 0 deletions fake-oauth/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* A *very* simple server, redirecting all requests to a specified url.
*/
const https = require("https");
const fs = require("fs");
const url = require("url");

const PORT = 5678;

// Read generated self-signed certificate to serve https.
// See the Dockerfile for details.
const options = {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem"),
};

console.log(`Starting a server at :${PORT}`);

https
.createServer(options, function (req, res) {
const q = url.parse(req.url, true).query;
const { redirect_uri } = q;

if (!redirect_uri) {
throw new Error("redirect_uri must be specified");
}

// The content doesn't matter - it's hardcoded in the server.
// The only important bit is the Location header - the backend should
// provide a url it would normally expect the oauth server to redirect to.
// NOTE: the url must be absolute, not docker friendly, i.e. it should
// start with "localhost", not "backend-project".
res.writeHead(302, { Location: redirect_uri });
res.end("Fake oauth reply");
})
.listen(PORT);

0 comments on commit ad94772

Please sign in to comment.