2 Commits

Author SHA1 Message Date
  alfred 1e4d63102f Add authentication selection 2 months ago
  alfred efd26a7a9f Update until basics 2 months ago

+ 3
- 0
.gitignore View File

@@ -10,3 +10,6 @@ node_modules
10 10
 **/__pycache__/*
11 11
 **/.sass-cache/*
12 12
 **/migrations/**
13
+
14
+djawth/settings/**
15
+!djawth/settings/dev.py

+ 5
- 0
apps/authentication/forms.py View File

@@ -0,0 +1,5 @@
1
+from django import forms
2
+
3
+
4
+class PasswordlessForm(forms.Form):
5
+    pass

+ 45
- 0
apps/authentication/models.py View File

@@ -0,0 +1,45 @@
1
+from dataclasses import dataclass
2
+from apps.authentication import forms
3
+
4
+
5
+@dataclass
6
+class Passwordless:
7
+    one_use_access: bool
8
+    web_hook_base: str
9
+    access_parameter: str
10
+
11
+
12
+class Authenticator:
13
+    alias = ''
14
+    model = None
15
+    default = None
16
+    template = ''
17
+    form = None
18
+
19
+    def __init__(self, **kwargs):
20
+        self.data = self.model(**kwargs) if kwargs else self.default
21
+
22
+    def __str__(self):
23
+        return self.alias
24
+
25
+
26
+class PasswordlessAuthenticator(Authenticator):
27
+    alias = 'Passwordless'
28
+    model = Passwordless
29
+    template = 'authentication/passwordless.html'
30
+    form = forms.PasswordlessForm
31
+    default = Passwordless(
32
+        one_use_access=True,
33
+        web_hook_base='https://example.com/auth',
34
+        access_parameter='access'
35
+    )
36
+
37
+
38
+class UserPasswordAuthenticator(Authenticator):
39
+    alias = 'User - Password'
40
+
41
+
42
+auth_methods = {
43
+    'passwordless': PasswordlessAuthenticator,
44
+    'user_password': UserPasswordAuthenticator
45
+}

+ 0
- 1
apps/authentication/models/__init__.py View File

@@ -1 +0,0 @@
1
-from .passwordless import Passwordless

+ 0
- 7
apps/authentication/models/passwordless.py View File

@@ -1,7 +0,0 @@
1
-from django.db import models
2
-from apps.base import models as base_models
3
-
4
-
5
-class Passwordless(models.Model):
6
-    user = models.ForeignKey(base_models.User, on_delete=models.CASCADE, related_name='passwordless')
7
-    token = models.CharField(max_length=50, blank=False, null=False)

apps/authentication/migrations/__init__.py → apps/authentication/templates/authentication/passwordless.html View File


+ 0
- 9
apps/authentication/urls.py View File

@@ -1,9 +0,0 @@
1
-#!/usr/bin/env python
2
-# -*- coding: utf-8 -*-
3
-
4
-from django.urls import path
5
-from django.views.generic import TemplateView
6
-from django.contrib.auth.views import LoginView, LogoutView
7
-
8
-urlpatterns = [
9
-]

+ 0
- 4
apps/base/models.py View File

@@ -4,7 +4,3 @@ from django.contrib.auth.models import AbstractUser
4 4
 
5 5
 class User(AbstractUser):
6 6
     email = models.EmailField('email address', blank=True, null=True)
7
-
8
-    @property
9
-    def full_name(self):
10
-        return self.get_full_name()

+ 6
- 3
apps/base/static/styles/scss/definitions.scss View File

@@ -1,5 +1,3 @@
1
-$primary-color-dark: #333;
2
-
3 1
 @font-face {
4 2
   font-family: SourceSansPro;
5 3
   src: url(../fonts/SourceSansPro/SourceSansPro-Regular.ttf);
@@ -11,4 +9,9 @@ $primary-color-dark: #333;
11 9
 }
12 10
 
13 11
 $primary-background-color: #1F2439;
14
-$secondary-background-color: #2E334A;
12
+$secondary-background-color: #2E334A;
13
+$alternative-background-color: #BA7C10;
14
+$link-color: #EBA728;
15
+
16
+$primary-color-dark: #333;
17
+$secondary-color: $secondary-background-color;

+ 27
- 13
apps/base/static/styles/scss/stylesheet.scss View File

@@ -6,6 +6,7 @@
6 6
 
7 7
 body {
8 8
     background-color: $primary-background-color;
9
+    min-height: 100vh;
9 10
 }
10 11
 
11 12
 header {
@@ -20,8 +21,28 @@ header {
20 21
     padding: 0 50px;
21 22
 }
22 23
 
23
-button {
24
-    color: #333;
24
+h2 {
25
+    font-size: 2rem;
26
+    text-decoration: underline;
27
+    font-family: Rubik, serif;
28
+    padding: 0;
29
+    margin: 1.5rem 0;
30
+}
31
+
32
+footer {
33
+    position: fixed;
34
+    bottom: 0;
35
+    width: 100%;
36
+    background-color: $alternative-background-color;
37
+    min-height: 20px;
38
+}
39
+
40
+input, button {
41
+    color: $primary-color-dark;
42
+}
43
+
44
+select {
45
+    display: inline-block;
25 46
 }
26 47
 
27 48
 .title {
@@ -55,25 +76,18 @@ button {
55 76
 }
56 77
 
57 78
 .content {
58
-    margin-top: 50px;
59 79
     background-color: #EEE;
60
-    color: $primary-background-color;
61 80
     padding: 25px;
62
-    border-radius: 25px;
81
+    border-radius: 0 25px 25px 0;
63 82
 
64
-    h2 {
65
-        font-size: 1.8rem;
66
-        font-family: Rubik, serif;
67
-        color: #333;
68
-        text-decoration: underline;
69
-        padding: 0;
70
-        margin: 0;
83
+    table, thead, tbody, tr, th, td {
84
+        color: $primary-background-color;
71 85
     }
72 86
 
73 87
     .no-projects {
74 88
         display: flex;
75 89
         height: 200px;
76
-        color: #333;
90
+        color: $primary-color-dark;
77 91
         align-items: center;
78 92
         padding-left: 25px;
79 93
     }

+ 82
- 71
apps/base/static/styles/stylesheet.css View File

@@ -2017,7 +2017,7 @@ ul:not(.browser-default) {
2017 2017
     list-style-type: none; }
2018 2018
 
2019 2019
 a {
2020
-  color: #039be5;
2020
+  color: #EBA728;
2021 2021
   text-decoration: none;
2022 2022
   -webkit-tap-highlight-color: transparent; }
2023 2023
 
@@ -2376,14 +2376,14 @@ td, th {
2376 2376
     .collection .collection-item:last-child {
2377 2377
       border-bottom: none; }
2378 2378
     .collection .collection-item.active {
2379
-      background-color: #26a69a;
2380
-      color: #eafaf9; }
2379
+      background-color: #2E334A;
2380
+      color: #bbc0d5; }
2381 2381
       .collection .collection-item.active .secondary-content {
2382 2382
         color: #fff; }
2383 2383
   .collection a.collection-item {
2384 2384
     display: block;
2385 2385
     transition: .25s;
2386
-    color: #26a69a; }
2386
+    color: #2E334A; }
2387 2387
     .collection a.collection-item:not(.active):hover {
2388 2388
       background-color: #ddd; }
2389 2389
   .collection.with-header .collection-header {
@@ -2397,7 +2397,7 @@ td, th {
2397 2397
 
2398 2398
 .secondary-content {
2399 2399
   float: right;
2400
-  color: #26a69a; }
2400
+  color: #2E334A; }
2401 2401
 
2402 2402
 .collapsible .collection {
2403 2403
   margin: 0;
@@ -2420,7 +2420,7 @@ td, th {
2420 2420
   height: 4px;
2421 2421
   display: block;
2422 2422
   width: 100%;
2423
-  background-color: #acece6;
2423
+  background-color: #8c94b8;
2424 2424
   border-radius: 2px;
2425 2425
   margin: 0.5rem 0 1rem 0;
2426 2426
   overflow: hidden; }
@@ -2429,10 +2429,10 @@ td, th {
2429 2429
     top: 0;
2430 2430
     left: 0;
2431 2431
     bottom: 0;
2432
-    background-color: #26a69a;
2432
+    background-color: #2E334A;
2433 2433
     transition: width .3s linear; }
2434 2434
   .progress .indeterminate {
2435
-    background-color: #26a69a; }
2435
+    background-color: #2E334A; }
2436 2436
     .progress .indeterminate:before {
2437 2437
       content: '';
2438 2438
       position: absolute;
@@ -2530,7 +2530,7 @@ span.badge {
2530 2530
     font-weight: 300;
2531 2531
     font-size: 0.8rem;
2532 2532
     color: #fff;
2533
-    background-color: #26a69a;
2533
+    background-color: #2E334A;
2534 2534
     border-radius: 2px; }
2535 2535
   span.badge.new:after {
2536 2536
     content: " new"; }
@@ -3761,18 +3761,18 @@ small {
3761 3761
 
3762 3762
 .btn:focus, .btn-large:focus, .btn-small:focus,
3763 3763
 .btn-floating:focus {
3764
-  background-color: #1d7d74; }
3764
+  background-color: #1a1d2b; }
3765 3765
 
3766 3766
 .btn, .btn-large, .btn-small {
3767 3767
   text-decoration: none;
3768 3768
   color: #fff;
3769
-  background-color: #26a69a;
3769
+  background-color: #2E334A;
3770 3770
   text-align: center;
3771 3771
   letter-spacing: .5px;
3772 3772
   transition: background-color .2s ease-out;
3773 3773
   cursor: pointer; }
3774 3774
   .btn:hover, .btn-large:hover, .btn-small:hover {
3775
-    background-color: #2bbbad; }
3775
+    background-color: #383e5a; }
3776 3776
 
3777 3777
 .btn-floating {
3778 3778
   display: inline-block;
@@ -3784,13 +3784,13 @@ small {
3784 3784
   height: 40px;
3785 3785
   line-height: 40px;
3786 3786
   padding: 0;
3787
-  background-color: #26a69a;
3787
+  background-color: #2E334A;
3788 3788
   border-radius: 50%;
3789 3789
   transition: background-color .3s;
3790 3790
   cursor: pointer;
3791 3791
   vertical-align: middle; }
3792 3792
   .btn-floating:hover {
3793
-    background-color: #26a69a; }
3793
+    background-color: #2E334A; }
3794 3794
   .btn-floating:before {
3795 3795
     border-radius: 0; }
3796 3796
   .btn-floating.btn-large {
@@ -3915,7 +3915,7 @@ button.btn-floating {
3915 3915
     z-index: -1;
3916 3916
     width: 40px;
3917 3917
     height: 40px;
3918
-    background-color: #26a69a;
3918
+    background-color: #2E334A;
3919 3919
     border-radius: 50%;
3920 3920
     transform: scale(0); }
3921 3921
 
@@ -3983,7 +3983,7 @@ button.btn-floating {
3983 3983
       height: 1px; }
3984 3984
     .dropdown-content li > a, .dropdown-content li > span {
3985 3985
       font-size: 16px;
3986
-      color: #26a69a;
3986
+      color: #2E334A;
3987 3987
       display: block;
3988 3988
       line-height: 22px;
3989 3989
       padding: 14px 16px; }
@@ -4343,11 +4343,11 @@ body.keyboard-focused .dropdown-content li:focus {
4343 4343
   -webkit-font-smoothing: antialiased; }
4344 4344
 
4345 4345
 select:focus {
4346
-  outline: 1px solid #c9f3ef; }
4346
+  outline: 1px solid #a2a9c5; }
4347 4347
 
4348 4348
 button:focus {
4349 4349
   outline: none;
4350
-  background-color: #2ab7a9; }
4350
+  background-color: #363c57; }
4351 4351
 
4352 4352
 label {
4353 4353
   font-size: 0.8rem;
@@ -4452,8 +4452,8 @@ textarea.materialize-textarea {
4452 4452
   input[type=number]:not(.browser-default):focus:not([readonly]),
4453 4453
   input[type=search]:not(.browser-default):focus:not([readonly]),
4454 4454
   textarea.materialize-textarea:focus:not([readonly]) {
4455
-    border-bottom: 1px solid #26a69a;
4456
-    box-shadow: 0 1px 0 0 #26a69a; }
4455
+    border-bottom: 1px solid #2E334A;
4456
+    box-shadow: 0 1px 0 0 #2E334A; }
4457 4457
   input:not([type]):focus:not([readonly]) + label,
4458 4458
   input[type=text]:not(.browser-default):focus:not([readonly]) + label,
4459 4459
   input[type=password]:not(.browser-default):focus:not([readonly]) + label,
@@ -4467,7 +4467,7 @@ textarea.materialize-textarea {
4467 4467
   input[type=number]:not(.browser-default):focus:not([readonly]) + label,
4468 4468
   input[type=search]:not(.browser-default):focus:not([readonly]) + label,
4469 4469
   textarea.materialize-textarea:focus:not([readonly]) + label {
4470
-    color: #26a69a; }
4470
+    color: #2E334A; }
4471 4471
   input:not([type]):focus.valid ~ label,
4472 4472
   input[type=text]:not(.browser-default):focus.valid ~ label,
4473 4473
   input[type=password]:not(.browser-default):focus.valid ~ label,
@@ -4749,7 +4749,7 @@ textarea.materialize-textarea + label:after, .select-wrapper + label:after {
4749 4749
     transition: color .2s;
4750 4750
     top: 0.5rem; }
4751 4751
     .input-field .prefix.active {
4752
-      color: #26a69a; }
4752
+      color: #2E334A; }
4753 4753
   .input-field .prefix ~ input,
4754 4754
   .input-field .prefix ~ textarea,
4755 4755
   .input-field .prefix ~ label,
@@ -4894,11 +4894,11 @@ textarea {
4894 4894
 [type="radio"]:checked + span:after,
4895 4895
 [type="radio"].with-gap:checked + span:before,
4896 4896
 [type="radio"].with-gap:checked + span:after {
4897
-  border: 2px solid #26a69a; }
4897
+  border: 2px solid #2E334A; }
4898 4898
 
4899 4899
 [type="radio"]:checked + span:after,
4900 4900
 [type="radio"].with-gap:checked + span:after {
4901
-  background-color: #26a69a; }
4901
+  background-color: #2E334A; }
4902 4902
 
4903 4903
 [type="radio"]:checked + span:after {
4904 4904
   transform: scale(1.02); }
@@ -4987,8 +4987,8 @@ textarea {
4987 4987
   height: 22px;
4988 4988
   border-top: 2px solid transparent;
4989 4989
   border-left: 2px solid transparent;
4990
-  border-right: 2px solid #26a69a;
4991
-  border-bottom: 2px solid #26a69a;
4990
+  border-right: 2px solid #2E334A;
4991
+  border-bottom: 2px solid #2E334A;
4992 4992
   transform: rotate(40deg);
4993 4993
   backface-visibility: hidden;
4994 4994
   transform-origin: 100% 100%; }
@@ -5004,7 +5004,7 @@ textarea {
5004 5004
   height: 22px;
5005 5005
   border-top: none;
5006 5006
   border-left: none;
5007
-  border-right: 2px solid #26a69a;
5007
+  border-right: 2px solid #2E334A;
5008 5008
   border-bottom: none;
5009 5009
   transform: rotate(90deg);
5010 5010
   backface-visibility: hidden;
@@ -5053,8 +5053,8 @@ textarea {
5053 5053
   top: 0;
5054 5054
   width: 20px;
5055 5055
   height: 20px;
5056
-  border: 2px solid #26a69a;
5057
-  background-color: #26a69a;
5056
+  border: 2px solid #2E334A;
5057
+  background-color: #2E334A;
5058 5058
   z-index: 0; }
5059 5059
 [type="checkbox"].filled-in.tabbed:focus + span:not(.lever):after {
5060 5060
   border-radius: 2px;
@@ -5062,8 +5062,8 @@ textarea {
5062 5062
   background-color: rgba(0, 0, 0, 0.1); }
5063 5063
 [type="checkbox"].filled-in.tabbed:checked:focus + span:not(.lever):after {
5064 5064
   border-radius: 2px;
5065
-  background-color: #26a69a;
5066
-  border-color: #26a69a; }
5065
+  background-color: #2E334A;
5066
+  border-color: #2E334A; }
5067 5067
 [type="checkbox"].filled-in:disabled:not(:checked) + span:not(.lever):before {
5068 5068
   background-color: transparent;
5069 5069
   border: 2px solid transparent; }
@@ -5091,11 +5091,11 @@ textarea {
5091 5091
   width: 0;
5092 5092
   height: 0; }
5093 5093
   .switch label input[type=checkbox]:checked + .lever {
5094
-    background-color: #84c7c1; }
5094
+    background-color: #7c7c7c; }
5095 5095
     .switch label input[type=checkbox]:checked + .lever:before, .switch label input[type=checkbox]:checked + .lever:after {
5096 5096
       left: 18px; }
5097 5097
     .switch label input[type=checkbox]:checked + .lever:after {
5098
-      background-color: #26a69a; }
5098
+      background-color: #2E334A; }
5099 5099
 
5100 5100
 .switch label .lever {
5101 5101
   content: "";
@@ -5120,7 +5120,7 @@ textarea {
5120 5120
     top: -3px;
5121 5121
     transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease; }
5122 5122
   .switch label .lever:before {
5123
-    background-color: rgba(38, 166, 154, 0.15); }
5123
+    background-color: rgba(46, 51, 74, 0.15); }
5124 5124
   .switch label .lever:after {
5125 5125
     background-color: #F1F1F1;
5126 5126
     box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); }
@@ -5128,7 +5128,7 @@ textarea {
5128 5128
 input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before,
5129 5129
 input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before {
5130 5130
   transform: scale(2.4);
5131
-  background-color: rgba(38, 166, 154, 0.15); }
5131
+  background-color: rgba(46, 51, 74, 0.15); }
5132 5132
 
5133 5133
 input[type=checkbox]:not(:disabled) ~ .lever:active:before,
5134 5134
 input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before {
@@ -5184,7 +5184,7 @@ select {
5184 5184
     user-select: none;
5185 5185
     z-index: 1; }
5186 5186
     .select-wrapper input.select-dropdown:focus {
5187
-      border-bottom: 1px solid #26a69a; }
5187
+      border-bottom: 1px solid #2E334A; }
5188 5188
   .select-wrapper .caret {
5189 5189
     position: absolute;
5190 5190
     right: 0;
@@ -5310,7 +5310,7 @@ input[type=range] + .thumb {
5310 5310
   height: 0;
5311 5311
   width: 0;
5312 5312
   border-radius: 50%;
5313
-  background-color: #26a69a;
5313
+  background-color: #2E334A;
5314 5314
   margin-left: 7px;
5315 5315
   transform-origin: 50% 50%;
5316 5316
   transform: rotate(-45deg); }
@@ -5318,7 +5318,7 @@ input[type=range] + .thumb {
5318 5318
     display: block;
5319 5319
     width: 30px;
5320 5320
     text-align: center;
5321
-    color: #26a69a;
5321
+    color: #2E334A;
5322 5322
     font-size: 0;
5323 5323
     transform: rotate(45deg); }
5324 5324
   input[type=range] + .thumb.active {
@@ -5342,15 +5342,15 @@ input[type=range]::-webkit-slider-thumb {
5342 5342
   height: 14px;
5343 5343
   width: 14px;
5344 5344
   border-radius: 50%;
5345
-  background: #26a69a;
5345
+  background: #2E334A;
5346 5346
   transition: box-shadow .3s;
5347 5347
   -webkit-appearance: none;
5348
-  background-color: #26a69a;
5348
+  background-color: #2E334A;
5349 5349
   transform-origin: 50% 50%;
5350 5350
   margin: -5px 0 0 0; }
5351 5351
 
5352 5352
 .keyboard-focused input[type=range]:focus:not(.active)::-webkit-slider-thumb {
5353
-  box-shadow: 0 0 0 10px rgba(38, 166, 154, 0.26); }
5353
+  box-shadow: 0 0 0 10px rgba(46, 51, 74, 0.26); }
5354 5354
 
5355 5355
 input[type=range] {
5356 5356
   /* fix for FF unable to apply focus style bug  */
@@ -5370,7 +5370,7 @@ input[type=range]::-moz-range-thumb {
5370 5370
   height: 14px;
5371 5371
   width: 14px;
5372 5372
   border-radius: 50%;
5373
-  background: #26a69a;
5373
+  background: #2E334A;
5374 5374
   transition: box-shadow .3s;
5375 5375
   margin-top: -5px; }
5376 5376
 
@@ -5379,7 +5379,7 @@ input[type=range]:-moz-focusring {
5379 5379
   outline-offset: -1px; }
5380 5380
 
5381 5381
 .keyboard-focused input[type=range]:focus:not(.active)::-moz-range-thumb {
5382
-  box-shadow: 0 0 0 10px rgba(38, 166, 154, 0.26); }
5382
+  box-shadow: 0 0 0 10px rgba(46, 51, 74, 0.26); }
5383 5383
 
5384 5384
 input[type=range]::-ms-track {
5385 5385
   height: 3px;
@@ -5400,11 +5400,11 @@ input[type=range]::-ms-thumb {
5400 5400
   height: 14px;
5401 5401
   width: 14px;
5402 5402
   border-radius: 50%;
5403
-  background: #26a69a;
5403
+  background: #2E334A;
5404 5404
   transition: box-shadow .3s; }
5405 5405
 
5406 5406
 .keyboard-focused input[type=range]:focus:not(.active)::-ms-thumb {
5407
-  box-shadow: 0 0 0 10px rgba(38, 166, 154, 0.26); }
5407
+  box-shadow: 0 0 0 10px rgba(46, 51, 74, 0.26); }
5408 5408
 
5409 5409
 /***************
5410 5410
     Nav List
@@ -5477,9 +5477,9 @@ input[type=range]::-ms-thumb {
5477 5477
     .sidenav li > a.btn-flat {
5478 5478
       color: #343434; }
5479 5479
     .sidenav li > a.btn:hover, .sidenav li > a.btn-large:hover, .sidenav li > a.btn-small:hover, .sidenav li > a.btn-large:hover {
5480
-      background-color: #2bbbad; }
5480
+      background-color: #383e5a; }
5481 5481
     .sidenav li > a.btn-floating:hover {
5482
-      background-color: #26a69a; }
5482
+      background-color: #2E334A; }
5483 5483
     .sidenav li > a > i, .sidenav li > a > [class^="mdi-"], .sidenav li > a li > a > [class*="mdi-"], .sidenav li > a > i.material-icons {
5484 5484
       float: left;
5485 5485
       height: 48px;
@@ -5630,7 +5630,7 @@ input[type=range]::-ms-thumb {
5630 5630
   width: 100%;
5631 5631
   height: 100%;
5632 5632
   opacity: 0;
5633
-  border-color: #26a69a; }
5633
+  border-color: #2E334A; }
5634 5634
 
5635 5635
 .spinner-blue,
5636 5636
 .spinner-blue-only {
@@ -6204,7 +6204,7 @@ input[type=range]::-ms-thumb {
6204 6204
 /* Date Display */
6205 6205
 .datepicker-date-display {
6206 6206
   flex: 1 auto;
6207
-  background-color: #26a69a;
6207
+  background-color: #2E334A;
6208 6208
   color: #fff;
6209 6209
   padding: 20px 22px;
6210 6210
   font-weight: 500; }
@@ -6241,9 +6241,9 @@ input[type=range]::-ms-thumb {
6241 6241
     border-radius: 50%;
6242 6242
     padding: 0; }
6243 6243
     .datepicker-table td.is-today {
6244
-      color: #26a69a; }
6244
+      color: #2E334A; }
6245 6245
     .datepicker-table td.is-selected {
6246
-      background-color: #26a69a;
6246
+      background-color: #2E334A;
6247 6247
       color: #fff; }
6248 6248
     .datepicker-table td.is-outside-current-month, .datepicker-table td.is-disabled {
6249 6249
       color: rgba(0, 0, 0, 0.3);
@@ -6260,7 +6260,7 @@ input[type=range]::-ms-thumb {
6260 6260
   cursor: pointer;
6261 6261
   color: inherit; }
6262 6262
   .datepicker-day-button:focus {
6263
-    background-color: rgba(43, 161, 150, 0.25); }
6263
+    background-color: rgba(49, 53, 71, 0.25); }
6264 6264
 
6265 6265
 /* Footer */
6266 6266
 .datepicker-footer {
@@ -6274,7 +6274,7 @@ input[type=range]::-ms-thumb {
6274 6274
 .datepicker-clear,
6275 6275
 .datepicker-today,
6276 6276
 .datepicker-done {
6277
-  color: #26a69a;
6277
+  color: #2E334A;
6278 6278
   padding: 0 1rem; }
6279 6279
 
6280 6280
 .datepicker-clear {
@@ -6314,7 +6314,7 @@ input[type=range]::-ms-thumb {
6314 6314
 /* Clock Digital Display */
6315 6315
 .timepicker-digital-display {
6316 6316
   flex: 1 auto;
6317
-  background-color: #26a69a;
6317
+  background-color: #2E334A;
6318 6318
   padding: 10px;
6319 6319
   font-weight: 300; }
6320 6320
 
@@ -6385,7 +6385,7 @@ input[type=range]::-ms-thumb {
6385 6385
 
6386 6386
 .timepicker-tick.active,
6387 6387
 .timepicker-tick:hover {
6388
-  background-color: rgba(38, 166, 154, 0.25); }
6388
+  background-color: rgba(46, 51, 74, 0.25); }
6389 6389
 
6390 6390
 .timepicker-dial {
6391 6391
   transition: transform 350ms, opacity 350ms; }
@@ -6400,7 +6400,7 @@ input[type=range]::-ms-thumb {
6400 6400
 .timepicker-canvas {
6401 6401
   transition: opacity 175ms; }
6402 6402
   .timepicker-canvas line {
6403
-    stroke: #26a69a;
6403
+    stroke: #2E334A;
6404 6404
     stroke-width: 4;
6405 6405
     stroke-linecap: round; }
6406 6406
 
@@ -6409,11 +6409,11 @@ input[type=range]::-ms-thumb {
6409 6409
 
6410 6410
 .timepicker-canvas-bearing {
6411 6411
   stroke: none;
6412
-  fill: #26a69a; }
6412
+  fill: #2E334A; }
6413 6413
 
6414 6414
 .timepicker-canvas-bg {
6415 6415
   stroke: none;
6416
-  fill: #26a69a; }
6416
+  fill: #2E334A; }
6417 6417
 
6418 6418
 /* Footer */
6419 6419
 .timepicker-footer {
@@ -6426,7 +6426,7 @@ input[type=range]::-ms-thumb {
6426 6426
   color: #F44336; }
6427 6427
 
6428 6428
 .timepicker-close {
6429
-  color: #26a69a; }
6429
+  color: #2E334A; }
6430 6430
 
6431 6431
 .timepicker-clear,
6432 6432
 .timepicker-close {
@@ -6455,7 +6455,8 @@ input[type=range]::-ms-thumb {
6455 6455
   color: #D8D9DB; }
6456 6456
 
6457 6457
 body {
6458
-  background-color: #1F2439; }
6458
+  background-color: #1F2439;
6459
+  min-height: 100vh; }
6459 6460
 
6460 6461
 header {
6461 6462
   background-color: #2E334A;
@@ -6467,9 +6468,26 @@ header {
6467 6468
   align-items: center;
6468 6469
   padding: 0 50px; }
6469 6470
 
6470
-button {
6471
+h2 {
6472
+  font-size: 2rem;
6473
+  text-decoration: underline;
6474
+  font-family: Rubik, serif;
6475
+  padding: 0;
6476
+  margin: 1.5rem 0; }
6477
+
6478
+footer {
6479
+  position: fixed;
6480
+  bottom: 0;
6481
+  width: 100%;
6482
+  background-color: #BA7C10;
6483
+  min-height: 20px; }
6484
+
6485
+input, button {
6471 6486
   color: #333; }
6472 6487
 
6488
+select {
6489
+  display: inline-block; }
6490
+
6473 6491
 .title h1 {
6474 6492
   font-family: Rubik, serif;
6475 6493
   font-size: 2rem;
@@ -6492,18 +6510,11 @@ button {
6492 6510
     padding: 20px; }
6493 6511
 
6494 6512
 .content {
6495
-  margin-top: 50px;
6496 6513
   background-color: #EEE;
6497
-  color: #1F2439;
6498 6514
   padding: 25px;
6499
-  border-radius: 25px; }
6500
-  .content h2 {
6501
-    font-size: 1.8rem;
6502
-    font-family: Rubik, serif;
6503
-    color: #333;
6504
-    text-decoration: underline;
6505
-    padding: 0;
6506
-    margin: 0; }
6515
+  border-radius: 0 25px 25px 0; }
6516
+  .content table, .content thead, .content tbody, .content tr, .content th, .content td {
6517
+    color: #1F2439; }
6507 6518
   .content .no-projects {
6508 6519
     display: flex;
6509 6520
     height: 200px;

+ 3
- 1
apps/base/templates/base.html View File

@@ -15,7 +15,6 @@
15 15
   <link rel="shortcut icon" href="favicon.ico" type="image/vnd.microsoft.icon">
16 16
   <link href="{% static 'fonts/MaterialIcons.css' %}" rel="stylesheet">
17 17
   <link rel="stylesheet" href="{% static 'styles/stylesheet.css' %}" type="text/css">
18
-  <script type="text/javascript" src="{% static 'js/materialize/bin/materialize.min.js' %}"></script>
19 18
   </head>
20 19
   <body>
21 20
   {% block body %}
@@ -38,6 +37,7 @@
38 37
     {% block precontent %}{% endblock %}
39 38
     <div class="container">
40 39
         {% block container %}
40
+            {% block title %}{% endblock %}
41 41
             <div class="content">
42 42
             {% block content %}
43 43
             {% endblock %}
@@ -45,5 +45,7 @@
45 45
         {% endblock %}
46 46
     </div>
47 47
   {% endblock %}
48
+  <footer>
49
+  </footer>
48 50
   </body>
49 51
 </html>

apps/projects/__init__.py → apps/project/__init__.py View File


+ 5
- 0
apps/project/apps.py View File

@@ -0,0 +1,5 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class ProjectConfig(AppConfig):
5
+    name = 'project'

+ 12
- 0
apps/project/forms.py View File

@@ -0,0 +1,12 @@
1
+from django import forms
2
+from apps.project.models import Project
3
+from apps.authentication.models import auth_methods
4
+
5
+
6
+class ConfirmProjectForm(forms.ModelForm):
7
+    class Meta:
8
+        model = Project
9
+        fields = '__all__'
10
+        exclude = ['auth_data']
11
+
12
+    auth_type = forms.ChoiceField(choices=[(key, auth.alias) for key, auth in auth_methods.items()])

+ 62
- 0
apps/project/models.py View File

@@ -0,0 +1,62 @@
1
+from django.db import models
2
+from apps.base import models as base_models
3
+from apps.authentication.models import auth_methods
4
+from django.utils.text import slugify
5
+from django.contrib.postgres.fields import JSONField
6
+
7
+
8
+class Project(models.Model):
9
+    name = models.CharField(max_length=255, blank=False, null=False)
10
+    owner = models.ForeignKey(base_models.User, on_delete=models.CASCADE, related_name='projects')
11
+    identifier = models.CharField(max_length=265, blank=False, null=False, unique=True)
12
+    secret = models.CharField(max_length=255, blank=False, null=False)
13
+    allow_signup = models.BooleanField(default=False)
14
+    created = models.DateTimeField(auto_now_add=True)
15
+    updated = models.DateTimeField(auto_now=True)
16
+    auth_type = models.CharField(max_length=50, blank=False, null=False, choices=[
17
+        (key, auth.alias) for key, auth in auth_methods.items()
18
+    ])
19
+    auth_data = JSONField(default=dict)
20
+
21
+    @classmethod
22
+    def get_identifier(cls, name, value=0):
23
+        _name = name if value == 0 else f'{name} {value}'
24
+        identifier = slugify(_name)
25
+        try:
26
+            Project.objects.get(identifier=identifier)
27
+            return Project.get_identifier(name, value + 1)
28
+        except Project.DoesNotExist:
29
+            return identifier
30
+
31
+    @property
32
+    def authentication(self):
33
+        return auth_methods[self.auth_type](**self.auth_data)
34
+
35
+
36
+class Identity(models.Model):
37
+    class Meta:
38
+        unique_together = ['email', 'project']
39
+    uuid = models.UUIDField(unique=True)
40
+    email = models.EmailField('email address', blank=True, null=True)
41
+    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='users')
42
+    user = models.ForeignKey(base_models.User, on_delete=models.CASCADE, related_name='credentials', null=True)
43
+    created = models.DateTimeField(auto_now_add=True)
44
+    updated = models.DateTimeField(auto_now=True)
45
+
46
+    @classmethod
47
+    def invite(cls, email, project):
48
+        pass
49
+
50
+    def has_logged_in(self):
51
+        return self.history.objects.count() > 0
52
+
53
+
54
+class Credential(models.Model):
55
+    identity = models.ForeignKey(Identity, on_delete=models.CASCADE, related_name='credentials')
56
+    created = models.DateTimeField(auto_now_add=True)
57
+    auth_data = JSONField(default=dict)
58
+
59
+
60
+class Access(models.Model):
61
+    identity = models.ForeignKey(Identity, on_delete=models.CASCADE, related_name='history')
62
+    date = models.DateTimeField(auto_now_add=True)

+ 29
- 0
apps/project/templates/projects/confirm_project.html View File

@@ -0,0 +1,29 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}
4
+<h2>Confirm project</h2>
5
+<div class="row">
6
+    <div class="col s2" style="font-weight: bold;">Name:</div>
7
+    <div class="col s10">{{ form.name.value }}</div>
8
+</div>
9
+<div class="row">
10
+    <div class="col s2" style="font-weight: bold;">Authentication type:</div>
11
+    <div class="col s10">{{ form.instance.get_auth_type_display }}</div>
12
+</div>
13
+{% endblock %}
14
+
15
+{% block content %}
16
+<form method="post" action="{% url 'confirm_project' %}">
17
+{% csrf_token %}
18
+<input type="hidden" name="name" value="{{ form.name.value }}" />
19
+<input type="hidden" name="secret" value="{{ form.secret.value }}" />
20
+<input type="hidden" name="auth_type" value="{{ form.auth_type.value }}">
21
+<div class="row">
22
+    Identifier: <input type="text" name="identifier" value="{{ form.identifier.value }}">
23
+</div>
24
+<div class="row">
25
+    <input type="submit" class="btn" value="Confirm">
26
+</div>
27
+</form>
28
+
29
+{% endblock %}

+ 29
- 0
apps/project/templates/projects/create_project.html View File

@@ -0,0 +1,29 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}
4
+<h2>Create project</h2>
5
+{% endblock %}
6
+
7
+{% block content %}
8
+<form method="post" action="{% url 'confirm_project' %}">
9
+{% csrf_token %}
10
+<div class="row">
11
+    Name: <input type="text" name="name">
12
+</div>
13
+<div class="row">
14
+    Secret: <input type="text" name="secret">
15
+</div>
16
+<div class="row">
17
+    Authentication type:
18
+    <select name="auth_type">
19
+        {% for key, value in auth_methods.items %}
20
+        <option value="{{ key }}">{{ value }}</option>
21
+        {% endfor %}
22
+    </select>
23
+</div>
24
+<div class="row">
25
+    <input type="submit" class="btn" value="Create">
26
+<//div>
27
+</form>
28
+
29
+{% endblock %}

+ 4
- 0
apps/project/templates/projects/project.html View File

@@ -0,0 +1,4 @@
1
+{% extends "base.html" %}
2
+
3
+{% block content %}
4
+{% endblock %}

+ 34
- 0
apps/project/templates/projects/projects_list.html View File

@@ -0,0 +1,34 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}
4
+<h2>Projects</h2>
5
+{% endblock %}
6
+
7
+{% block content %}
8
+{% if user.projects.count < 1 %}
9
+<div class="no-projects">
10
+No projects
11
+</div>
12
+{% else %}
13
+<table class="stripped" style="margin: 20px 0;">
14
+    <thead>
15
+    <tr>
16
+      <th>Project</th>
17
+      <th>Authentication</th>
18
+      <th>Authorization</th>
19
+    </tr>
20
+    </thead>
21
+
22
+    <tbody>
23
+    {% for project in user.projects.all %}
24
+    <tr>
25
+        <td><a href="#!" style="display: inline-block; font-weight: bold; width: 100%;">{{ project.name }}</a></td>
26
+        <td><a class="btn-flat">{{ project.authentication }}</a></td>
27
+        <td><a class="btn-flat">JWT</a></td>
28
+    </tr>
29
+    {% endfor %}
30
+    </tbody>
31
+</table>
32
+{% endif %}
33
+<a class="btn" href="{% url 'new_project' %}"><i class="material-icons left">add_circle</i>Create project</a>
34
+{% endblock %}

+ 26
- 0
apps/project/urls.py View File

@@ -0,0 +1,26 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+
4
+from django.urls import path
5
+from django.views.generic import TemplateView
6
+from django.contrib.auth.decorators import login_required
7
+from .views import ConfirmProjectView
8
+from apps.authentication.models import auth_methods
9
+
10
+
11
+urlpatterns = [
12
+    path('home', login_required(TemplateView.as_view(
13
+        template_name='projects/projects_list.html'
14
+    )), name='home'),
15
+    path('new_project', login_required(TemplateView.as_view(
16
+        template_name='projects/create_project.html',
17
+        extra_context={
18
+            'auth_methods': lambda: {key: value.alias for key, value in auth_methods.items()}
19
+        }
20
+    )), name='new_project'),
21
+    path('confirm_project', login_required(ConfirmProjectView.as_view()), name='confirm_project'),
22
+    # path('<project>/authentication/<slug>', login_required(UpdateView(
23
+    # )), name='edit_authentication'),
24
+    # path('edit_project/<slug>', login_required(UpdateView(
25
+    # )), name='project'),
26
+]

+ 32
- 0
apps/project/views.py View File

@@ -0,0 +1,32 @@
1
+from django.views.generic import CreateView
2
+from django.http import QueryDict
3
+from .forms import ConfirmProjectForm
4
+
5
+
6
+class ConfirmProjectView(CreateView):
7
+    template_name = 'projects/confirm_project.html'
8
+    success_url = 'home'
9
+    form_class = ConfirmProjectForm
10
+
11
+    def get_form_kwargs(self):
12
+        from .models import Project
13
+        kwargs = super().get_form_kwargs()
14
+        identifier = self.request.POST.get('identifier', self.request.POST['name'])
15
+
16
+        post_data = QueryDict(mutable=True)
17
+        post_data.update(self.request.POST)
18
+        post_data.update({
19
+            'owner': self.request.user,
20
+            'identifier': Project.get_identifier(identifier)
21
+        })
22
+
23
+        kwargs.update({'data': post_data})
24
+        return kwargs
25
+
26
+    def form_valid(self, form):
27
+        if 'new_project' in self.request.META['HTTP_REFERER']:
28
+            return super().form_invalid(form)
29
+        return super().form_valid(form)
30
+
31
+    def form_invalid(self, form):
32
+        return super().form_invalid(form)

+ 0
- 5
apps/projects/apps.py View File

@@ -1,5 +0,0 @@
1
-from django.apps import AppConfig
2
-
3
-
4
-class ProjectsConfig(AppConfig):
5
-    name = 'projects'

+ 0
- 0
apps/projects/migrations/__init__.py View File


+ 0
- 29
apps/projects/models.py View File

@@ -1,29 +0,0 @@
1
-from django.db import models
2
-from apps.base import models as base_models
3
-from django.contrib.auth.validators import UnicodeUsernameValidator
4
-from django.utils.translation import gettext_lazy as _
5
-
6
-
7
-class Project(models.Model):
8
-    name = models.CharField(max_length=255, blank=False, null=False)
9
-    owner = models.ForeignKey(base_models.User, on_delete=models.CASCADE, related_name='projects')
10
-    secret = models.CharField(max_length=255, blank=False, null=False)
11
-
12
-
13
-class User(models.Model):
14
-    class Meta:
15
-        unique_together = ['username', 'project']
16
-    username_validator = UnicodeUsernameValidator()
17
-    username = models.CharField(
18
-            _('username'),
19
-            max_length=50,
20
-            unique=False,
21
-            help_text=_('Required. 50 characters or fewer. Letters, digits and @/./+/-/_ only.'),
22
-            validators=[username_validator],
23
-            error_messages={
24
-                'unique': _("A user with that username already exists."),
25
-            },
26
-        )
27
-    password = models.CharField(_('password'), max_length=128)
28
-    last_login = models.DateTimeField(_('last login'), blank=True, null=True)
29
-    project = models.ForeignKey(base_models.User, on_delete=models.CASCADE, related_name='users')

+ 0
- 11
apps/projects/templates/projects/home.html View File

@@ -1,11 +0,0 @@
1
-{% extends "base.html" %}
2
-
3
-{% block content %}
4
-<h2>Projects</h2>
5
-{% if user.projects.count < 1 %}
6
-<div class="no-projects">
7
-No projects
8
-</div>
9
-{% endif %}
10
-<a class="waves-effect waves-light btn"><i class="material-icons left">add_circle</i>Create project</a>
11
-{% endblock %}

+ 0
- 12
apps/projects/urls.py View File

@@ -1,12 +0,0 @@
1
-#!/usr/bin/env python
2
-# -*- coding: utf-8 -*-
3
-
4
-from django.urls import path
5
-from django.views.generic import TemplateView
6
-from django.contrib.auth.decorators import login_required
7
-
8
-urlpatterns = [
9
-    path('home', login_required(TemplateView.as_view(
10
-        template_name='projects/home.html'
11
-    )), name='home')
12
-]

+ 4
- 4
djawth/settings/dev.py View File

@@ -32,14 +32,14 @@ ALLOWED_HOSTS = []
32 32
 # Application definition
33 33
 
34 34
 INSTALLED_APPS = [
35
+    'apps.base',
36
+    'apps.project',
37
+    # 'apps.authentication',
35 38
     'django.contrib.auth',
36 39
     'django.contrib.contenttypes',
37 40
     'django.contrib.sessions',
38 41
     'django.contrib.messages',
39 42
     'django.contrib.staticfiles',
40
-    'apps.base',
41
-    'apps.projects',
42
-    'apps.authentication'
43 43
 ]
44 44
 
45 45
 MIDDLEWARE = [
@@ -79,7 +79,7 @@ WSGI_APPLICATION = 'djawth.wsgi.application'
79 79
 DATABASES = {
80 80
     'default': {
81 81
         'ENGINE': 'django.db.backends.postgresql',
82
-        'NAME': 'trackmanager',
82
+        'NAME': 'djawth',
83 83
         'USER': 'postgres',
84 84
         'PASSWORD': 'mysecretpassword',
85 85
         'HOST': os.environ.get('DB_HOST', '127.0.0.1'),

+ 1
- 2
djawth/urls.py View File

@@ -17,6 +17,5 @@ from django.urls import path, include
17 17
 
18 18
 urlpatterns = [
19 19
     path('', include('apps.base.urls')),
20
-    path('', include('apps.projects.urls')),
21
-    path('', include('apps.authentication.urls'))
20
+    path('', include('apps.project.urls'))
22 21
 ]

Loading…
Cancel
Save