Add social auth Github + login page

This commit is contained in:
Loïc Gremaud 2024-10-04 01:01:36 +02:00
parent d7f0d2a7f2
commit 63897fb7b0
Signed by: Legrems
GPG Key ID: D4620E6DF3E0121D
14 changed files with 210 additions and 55 deletions

View File

@ -20,20 +20,13 @@ import os
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production SECRET_KEY = "django-insecure-changeme"
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! DEBUG = False
SECRET_KEY = "django-insecure-$440wv7cqb$-umfo-x%w_@p3g5kuuk1(!rv#=7*gzndx4_h4ds"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
@ -43,6 +36,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django_js_reverse", "django_js_reverse",
"djangobower", "djangobower",
"social_django",
"main", "main",
"users", "users",
"items", "items",
@ -50,13 +44,14 @@ INSTALLED_APPS = [
STATIC_URL = "/static/" STATIC_URL = "/static/"
LOGIN_URL = "/"
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static_source"),) STATICFILES_DIRS = (os.path.join(BASE_DIR, "static_source"),)
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder",
"djangobower.finders.BowerFinder", "djangobower.finders.BowerFinder",
# "compressor.finders.CompressorFinder",
) )
STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_ROOT = os.path.join(BASE_DIR, "static")
@ -64,7 +59,6 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static")
STORAGES = { STORAGES = {
"staticfiles": { "staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
# "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
} }
} }
@ -121,15 +115,8 @@ TEMPLATES = [
WSGI_APPLICATION = "app.wsgi.application" WSGI_APPLICATION = "app.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {} DATABASES = {}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
@ -146,9 +133,6 @@ AUTH_PASSWORD_VALIDATORS = [
] ]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us" LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC" TIME_ZONE = "UTC"
@ -160,6 +144,23 @@ USE_TZ = True
SIMPLE_HISTORY_HISTORY_ID_USE_UUID = True SIMPLE_HISTORY_HISTORY_ID_USE_UUID = True
SOCIAL_AUTH_JSONFIELD_ENABLED = True
AUTHENTICATION_BACKENDS = (
# "social_core.backends.open_id.OpenIdAuth",
# "social_core.backends.google.GoogleOpenId",
# "social_core.backends.google.GoogleOAuth2",
# "social_core.backends.google.GoogleOAuth",
# "social_core.backends.twitter.TwitterOAuth",
"social_core.backends.github.GithubOAuth2",
# "social_core.backends.yahoo.YahooOpenId",
# "django.contrib.auth.backends.ModelBackend",
)
SOCIAL_AUTH_GITHUB_KEY = ""
SOCIAL_AUTH_GITHUB_SECRET = ""
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True
SOCIAL_AUTH_LOGIN_REDIRECT_URL = "/"
from app.settingsLocal import * from app.settingsLocal import *

View File

@ -14,8 +14,11 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django_js_reverse.views import urls_js from django_js_reverse.views import urls_js
@ -24,5 +27,6 @@ urlpatterns = [
path("items/", include("items.urls")), path("items/", include("items.urls")),
path("users/", include("users.urls")), path("users/", include("users.urls")),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path('reverse.js', urls_js, name='reverse_js'), path("social/", include("social_django.urls", namespace="social")),
path("reverse.js", urls_js, name="reverse_js"),
] ]

View File

@ -0,0 +1,24 @@
# Generated by Django 5.1.1 on 2024-10-03 21:02
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("items", "0006_historicalitem_historicalitemrelation_and_more"),
]
operations = [
migrations.AlterField(
model_name="linkedproperty",
name="property",
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="items.property"),
),
migrations.AlterField(
model_name="relationproperty",
name="property",
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="items.property"),
),
]

View File

@ -89,7 +89,7 @@ class BaseLinkedProperty(ItemBase):
class Encryption(ItemBase.Encryption): class Encryption(ItemBase.Encryption):
fields = ItemBase.Encryption.fields + ["value"] fields = ItemBase.Encryption.fields + ["value"]
property = models.ForeignKey(Property, on_delete=models.CASCADE) property = models.ForeignKey(Property, on_delete=models.PROTECT)
# Value is encrypted too # Value is encrypted too
value = models.TextField(max_length=2048) value = models.TextField(max_length=2048)

View File

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
<div class="card bg-warning mt-4 pt-2 ps-lg-2" v-if="ready"> <div class="card bg-warning mt-4 pt-2 ps-lg-2">
<h5 class="card-header">{% trans "K356 is locked" %}</h5> <h5 class="card-header">{% trans "K356 is locked" %}</h5>
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{% trans "K356 needs an unlock..." %}</h5> <h5 class="card-title">{% trans "K356 needs an unlock..." %}</h5>
@ -8,7 +8,3 @@
<input class="form-control" type="password" v-model="password" @keyup.enter="generate_aes_key(password)" autofocus> <input class="form-control" type="password" v-model="password" @keyup.enter="generate_aes_key(password)" autofocus>
</div> </div>
</div> </div>
<div class="card bg-danger mt-4 pt-2 ps-lg-2 d-flex" v-else>
<h5 class="card-header flex-grow-1 flex-shrink-1 text-center">{% trans "Cannot load the application. Webcrypto must be enabled" %}</h5>
</div>

View File

@ -9,10 +9,6 @@ Loading = {
} }
}, },
mounted: function() {
this.generate_aes_key("asd")
},
methods: { methods: {
async generate_aes_key (password) { async generate_aes_key (password) {

View File

@ -1,7 +1,8 @@
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth.decorators import login_required
@login_required
def home(request): def home(request):
if request.user.is_authenticated:
return render(request, "base.html", {}) return render(request, "base.html", {})
return render(request, "login.html", {})

View File

@ -63,6 +63,7 @@ python = "^3.11"
Django = "^5.0" Django = "^5.0"
django-bower = {git = "https://github.com/ArcaniteSolutions/django-bower.git"} django-bower = {git = "https://github.com/ArcaniteSolutions/django-bower.git"}
django-simple-history = "^3.7" django-simple-history = "^3.7"
social-auth-app-django = "^5.4"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^24.4.0" black = "^24.4.0"

View File

@ -41,8 +41,8 @@
<nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark"> <nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
<div class="container-fluid"> <div class="container-fluid">
<v-btn color="navbar-brand" text @click="lock_me"> <v-btn outlined dark text @click="lock_me">
<v-icon small class="mr-2">mdi-lock</v-icon> <v-icon small color="white" class="mr-2">mdi-lock</v-icon>
{% trans "Lock" %} {% trans "Lock" %}
</v-btn> </v-btn>
@ -61,21 +61,19 @@
<li class="nav-item"> <li class="nav-item">
<router-link class="nav-link" to="/EncryptionTesting">{% trans "Encryption" %}</router-link> <router-link class="nav-link" to="/EncryptionTesting">{% trans "Encryption" %}</router-link>
</li> </li>
{% if request.user.is_superuser %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "More" %}</a> <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "More" %}</a>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="{% url 'users:logout' %}">{% trans "Logout" %}</a>
<a class="dropdown-item" href="#">Another action</a> {% if request.user.is_superuser %}
<a class="dropdown-item" href="#">Something else here</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" href="/admin/">{% trans "Admin" %}</a> <a class="dropdown-item" href="/admin/">{% trans "Admin" %}</a>
{% endif %}
</div> </div>
</li> </li>
{% endif %}
</ul> </ul>
<form class="d-flex"> <form class="d-flex">
<input class="form-control me-sm-2" type="search" placeholder="Search"> <input class="form-control me-sm-2" type="search" placeholder="Search" v-model="search">
<button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button> <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
</form> </form>
</div> </div>
@ -83,10 +81,17 @@
</nav> </nav>
<v-container class="container-xs"> <v-container class="container-xs">
<div class="card bg-danger mt-4 pt-2 ps-lg-2 d-flex" v-if="!ready">
<h5 class="card-header flex-grow-1 flex-shrink-1 text-center">{% trans "Cannot load the application. Webcrypto must be enabled" %}</h5>
</div>
<Loading @update_key="update_key" v-if="locked"></Loading> <Loading @update_key="update_key" v-if="locked"></Loading>
<template v-if="!locked"> <template v-if="!locked">
<router-view></router-view> <router-view></router-view>
</template> </template>
</v-container> </v-container>
</div> </div>

101
k356/templates/login.html Normal file
View File

@ -0,0 +1,101 @@
{% load i18n static %}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}</title>
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}">
<script src="{% static "vue/index.js" %}"></script>
<script src="{% static "sweetalert2/index" %}"></script>
<script src="{% static "vue-resource/index" %}"></script>
<script src="{% static "js.cookie.min/index.js" %}"></script>
<script src="{% static "vue-router/index.js" %}"></script>
<link href="{% static "bootstrap.min/index.css" %}" rel="stylesheet">
<script src="{% static "bootstrap.bundle.min/index.js" %}"></script>
<link href="{% static "bootswatch.min.css/index.css" %}" rel="stylesheet">
<script src="{% url 'reverse_js' %}" type="text/javascript"></script>
<link href="{% static "vuetify.min/index.css" %}" rel="stylesheet">
<script src="{% static "vuetify/index.js" %}"></script>
<script src="{% static "vuex/index.js" %}"></script>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
<link href="{% static "jetbrains-mono/index" %}">
<style>
.font {
font-family: 'JetBrains Mono', sans-serif;
}
</style>
</head>
<body>
<v-app class="font">
<div id="main" data-app>
<v-content>
<v-container class="container-xs">
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="290">
<v-card>
<v-card-title class="text-h5">
{% trans "Login" %}
</v-card-title>
<v-card-text>
{% trans "Select login option" %}
<v-list-item>
<v-list-item-content>
<v-btn tile color="primary" dark class="mb-2" href="{% url 'social:begin' 'github' %}">
{% trans "Github" %}
</v-btn>
</v-list-item-content>
</v-list-item>
</v-card-text>
</v-card>
</v-dialog>
</v-row>
</v-container>
</v-content>
</div>
</v-app>
<script type="text/javascript">
{% include 'scripts.js' %}
</script>
{% for name, path in templates.items %}
<script type="text/x-template" id="{{ name }}">
{% include path %}
</script>
{% endfor %}
<script type="text/javascript">
{% include "vue/login.js" %}
</script>
<script type="text/javascript">
function refresh_csrftoken() {
var csrftoken = Cookies.get('csrftoken');
Vue.http.headers.common['X-CSRFTOKEN'] = csrftoken;
}
refresh_csrftoken();
</script>
</body>
</html>

View File

@ -35,7 +35,8 @@ const approuter = new Vue({
el: "#main", el: "#main",
data: { data: {
uuid: "{{ user_settings.id }}", uuid: "{{ user_settings.id }}",
ready: null, ready: false,
search: '',
}, },
computed: { computed: {
@ -50,8 +51,6 @@ const approuter = new Vue({
Swal.fire({title: "{{_('The application cannot be launched. Webcrypto is not available.<br><br>Try another browser!') | escapejs}}", icon: "error", showConfirmButton: false}) Swal.fire({title: "{{_('The application cannot be launched. Webcrypto is not available.<br><br>Try another browser!') | escapejs}}", icon: "error", showConfirmButton: false})
this.ready = false
} else { } else {
// Try to generate a random keyPair and encrypting stuff before accepting the client // Try to generate a random keyPair and encrypting stuff before accepting the client
@ -65,8 +64,6 @@ const approuter = new Vue({
Swal.fire({title: "{{_('An error occured during testing Webcrypto. Please use a compatible browser.') | escapejs}}", icon: "error", showConfirmButton: false}) Swal.fire({title: "{{_('An error occured during testing Webcrypto. Please use a compatible browser.') | escapejs}}", icon: "error", showConfirmButton: false})
this.ready = false
} }
} }
@ -99,8 +96,8 @@ const approuter = new Vue({
await this.$http.post( await this.$http.post(
Urls["users:keys"](), Urls["users:keys"](),
{ {
privateKey: await this.wrapKey(this.keyPair.privateKey, aes_key, iv_private), privateKey: await this.wrapKey(keyPair.privateKey, aes_key, iv_private),
publicKey: await this.wrapKey(this.keyPair.publicKey, aes_key, iv_public), publicKey: await this.wrapKey(keyPair.publicKey, aes_key, iv_public),
} }
) )
@ -118,6 +115,8 @@ const approuter = new Vue({
Swal.fire({title: "{{_('Error during unwrapping of private key.<br><br>Maybe your password is wrong?') | escapejs}}", icon: "error", showConfirmButton: false}) Swal.fire({title: "{{_('Error during unwrapping of private key.<br><br>Maybe your password is wrong?') | escapejs}}", icon: "error", showConfirmButton: false})
console.error(err)
} }
}, },

View File

@ -0,0 +1,17 @@
{% include "vue/plugins.js" %}
Vue.config.devtools = true
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(EncryptionPlugin)
Vue.config.delimiters = ["[[", "]]"];
const approuter = new Vue({
vuetify: new Vuetify(),
el: "#main",
data: {
dialog: true,
},
})

View File

@ -8,5 +8,6 @@ app_name = "users"
urlpatterns = [ urlpatterns = [
path("keys", views.keys, name="keys"), path("keys", views.keys, name="keys"),
path("logout", views.ulogout, name="logout"),
path("k356/validate", views.k356_validate, name="k356.validate"), path("k356/validate", views.k356_validate, name="k356.validate"),
] ]

View File

@ -1,5 +1,7 @@
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -75,3 +77,10 @@ def keys(request):
"publicKey": us.public_key, "publicKey": us.public_key,
} }
) )
@login_required
def ulogout(request):
logout(request)
return redirect("main:home")