Browse Source

added login and logout view

funding-tag
Holger Frey 5 years ago
parent
commit
89c1760e9c
  1. 2
      development.ini
  2. BIN
      ordr3.sqlite
  3. 4
      ordr3/__init__.py
  4. 3
      ordr3/static/style.css
  5. 154
      ordr3/static/theme.css
  6. 16
      ordr3/templates/layout.jinja2
  7. 49
      ordr3/templates/root/login.jinja2
  8. 62
      ordr3/views/root.py

2
development.ini

@ -23,7 +23,7 @@ auth.secret = "change me for production" @@ -23,7 +23,7 @@ auth.secret = "change me for production"
session.secret = "change me for production"
session.auto_csrf = true
static_views.cache_max_age = 0
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.

BIN
ordr3.sqlite

Binary file not shown.

4
ordr3/__init__.py

@ -9,6 +9,8 @@ __version__ = "0.0.1" @@ -9,6 +9,8 @@ __version__ = "0.0.1"
from pyramid.config import Configurator
from pyramid.session import JSONSerializer, SignedCookieSessionFactory
from . import resources
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
@ -23,7 +25,7 @@ def main(global_config, **settings): @@ -23,7 +25,7 @@ def main(global_config, **settings):
require_csrf=settings["session.auto_csrf"]
)
# config.set_root_factory(root_factory)
config.set_root_factory(resources.Root)
config.include(".adapters")
config.include(".resources")

3
ordr3/static/style.css

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
.o3-login-card {
margin-top:7em;
}

154
ordr3/static/theme.css

@ -1,154 +0,0 @@ @@ -1,154 +0,0 @@
@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
body {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
color: #ffffff;
background: #bc2131;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
}
p {
font-weight: 300;
}
.font-normal {
font-weight: 400;
}
.font-semi-bold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.starter-template {
margin-top: 250px;
}
.starter-template .content {
margin-left: 10px;
}
.starter-template .content h1 {
margin-top: 10px;
font-size: 60px;
}
.starter-template .content h1 .smaller {
font-size: 40px;
color: #f2b7bd;
}
.starter-template .content .lead {
font-size: 25px;
color: #f2b7bd;
}
.starter-template .content .lead .font-normal {
color: #ffffff;
}
.starter-template .links {
float: right;
right: 0;
margin-top: 125px;
}
.starter-template .links ul {
display: block;
padding: 0;
margin: 0;
}
.starter-template .links ul li {
list-style: none;
display: inline;
margin: 0 10px;
}
.starter-template .links ul li:first-child {
margin-left: 0;
}
.starter-template .links ul li:last-child {
margin-right: 0;
}
.starter-template .links ul li.current-version {
color: #f2b7bd;
font-weight: 400;
}
.starter-template .links ul li a, a {
color: #f2b7bd;
text-decoration: underline;
}
.starter-template .links ul li a:hover, a:hover {
color: #ffffff;
text-decoration: underline;
}
.starter-template .links ul li .icon-muted {
color: #eb8b95;
margin-right: 5px;
}
.starter-template .links ul li:hover .icon-muted {
color: #ffffff;
}
.starter-template .copyright {
margin-top: 10px;
font-size: 0.9em;
color: #f2b7bd;
text-transform: lowercase;
float: right;
right: 0;
}
@media (max-width: 1199px) {
.starter-template .content h1 {
font-size: 45px;
}
.starter-template .content h1 .smaller {
font-size: 30px;
}
.starter-template .content .lead {
font-size: 20px;
}
}
@media (max-width: 991px) {
.starter-template {
margin-top: 0;
}
.starter-template .logo {
margin: 40px auto;
}
.starter-template .content {
margin-left: 0;
text-align: center;
}
.starter-template .content h1 {
margin-bottom: 20px;
}
.starter-template .links {
float: none;
text-align: center;
margin-top: 60px;
}
.starter-template .copyright {
float: none;
text-align: center;
}
}
@media (max-width: 767px) {
.starter-template .content h1 .smaller {
font-size: 25px;
display: block;
}
.starter-template .content .lead {
font-size: 16px;
}
.starter-template .links {
margin-top: 40px;
}
.starter-template .links ul li {
display: block;
margin: 0;
}
.starter-template .links ul li .icon-muted {
display: none;
}
.starter-template .copyright {
margin-top: 20px;
}
}

16
ordr3/templates/layout.jinja2

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Ordr</title>
<link href="{{request.static_url('ordr3:static/favicon.ico')}}" type="image/x-icon" rel="shortcut icon">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="{{request.static_url('ordr3:static/style.css')}}" type="text/css" media="screen" />
</head>
<body>
</body>
</html>

49
ordr3/templates/root/login.jinja2

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Ordr - Log In</title>
<link href="{{request.static_url('ordr3:static/favicon.ico')}}" type="image/x-icon" rel="shortcut icon">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="{{request.static_url('ordr3:static/style.css')}}" type="text/css" media="screen" />
</head>
<body>
<div class="container">
<div class="row o3-login-card">
<div class="col"></div>
<div class="col">
<div class="card" style="width: 18rem;">
<div class="card-header bg-dark text-light">
<h2 class="card-title text-center pt-1">Ordr</h5>
</div>
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted text-center mt-1 mb-4">Please Log In</h6>
<form method="post" action="{{request.resource_url(request.root, 'login')}}" class="form">
<div class="form-group">
<label for="username" class="sr-only">Username</label>
<input type="text" name="username" id="username" class="form-control text-center {% if error %}is-invalid{% endif %}" placeholder="Username" required="required">
</div>
<div class="form-group">
<label for="password" class="sr-only">Password</label>
<input type="password" name="password" id="password" class="form-control text-center {% if error %}is-invalid{% endif %}" placeholder="Password" required="required">
<div class="invalid-feedback text-center">Credentials are invalid</div>
</div>
<div class="form-group">
<button type="submit" name="submit" value="login" class="form-control btn-primary">Log In</button>
</div>
</form>
<ul class="list-group list-group-flush text-center">
<li class="list-group-item"><a href="{{request.resource_url(request.root, 'forgot')}}" class="text-secondary">Forgot your password?</a></li>
<li class="list-group-item"><a href="{{request.resource_url(request.root, 'register')}}" class="text-secondary">Register a new account.</a></li>
</ul>
</div>
</div>
</div>
<div class="col"></div>
</div>
</div>
</body>
</html>

62
ordr3/views/root.py

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
""" static and login pages """
from pyramid.view import view_config
from pyramid.security import forget, remember
from pyramid.httpexceptions import HTTPFound
from .. import security, services
@view_config(
context="ordr3:resources.Root", permission="view",
)
def root(context, request):
if request.user:
return HTTPFound(request.resource_path(request.root, "orders"))
else:
return HTTPFound(request.resource_path(request.root, "login"))
@view_config(
context="ordr3:resources.Root",
name="login",
permission="view",
request_method="GET",
renderer="ordr3:templates/root/login.jinja2",
)
def login(context, request):
return {"error": False}
@view_config(
context="ordr3:resources.Root",
name="login",
permission="view",
request_method="POST",
require_csrf=False,
renderer="ordr3:templates/root/login.jinja2",
)
def check_credentials(context, request):
username = request.POST.get("username", "")
password = request.POST.get("password", "")
crypt_context = security.get_passlib_context()
user = services.verify_credentials(
request.repo, crypt_context, username, password
)
if user is not None and user.is_active:
headers = remember(request, user.id)
return HTTPFound(
request.resource_path(request.root, "orders"), headers=headers
)
return {"error": True}
@view_config(context="ordr3:resources.Root", name="logout", permission="view")
def logout(context, request):
""" logout of a user """
headers = forget(request)
return HTTPFound(
request.resource_path(request.root, "login"), headers=headers
)
Loading…
Cancel
Save