mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
Changed the behavior of the JWT LiveValidityWindows
Added milligram.css to some samples
This commit is contained in:
parent
34bc5e0638
commit
a4381ec719
602
samples/apachemodule/Apache24/htdocs/css/milligram.css
Normal file
602
samples/apachemodule/Apache24/htdocs/css/milligram.css
Normal file
@ -0,0 +1,602 @@
|
||||
/*!
|
||||
* Milligram v1.3.0
|
||||
* https://milligram.github.io
|
||||
*
|
||||
* Copyright (c) 2017 CJ Patoilo
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #606c76;
|
||||
font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
font-size: 1.6em;
|
||||
font-weight: 300;
|
||||
letter-spacing: .01em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 0.3rem solid #d1d1d1;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
blockquote *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.button,
|
||||
button,
|
||||
input[type='button'],
|
||||
input[type='reset'],
|
||||
input[type='submit'] {
|
||||
background-color: #9b4dca;
|
||||
border: 0.1rem solid #9b4dca;
|
||||
border-radius: .4rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
height: 3.8rem;
|
||||
letter-spacing: .1rem;
|
||||
line-height: 3.8rem;
|
||||
padding: 0 3.0rem;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.button:focus, .button:hover,
|
||||
button:focus,
|
||||
button:hover,
|
||||
input[type='button']:focus,
|
||||
input[type='button']:hover,
|
||||
input[type='reset']:focus,
|
||||
input[type='reset']:hover,
|
||||
input[type='submit']:focus,
|
||||
input[type='submit']:hover {
|
||||
background-color: #606c76;
|
||||
border-color: #606c76;
|
||||
color: #fff;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.button[disabled],
|
||||
button[disabled],
|
||||
input[type='button'][disabled],
|
||||
input[type='reset'][disabled],
|
||||
input[type='submit'][disabled] {
|
||||
cursor: default;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.button[disabled]:focus, .button[disabled]:hover,
|
||||
button[disabled]:focus,
|
||||
button[disabled]:hover,
|
||||
input[type='button'][disabled]:focus,
|
||||
input[type='button'][disabled]:hover,
|
||||
input[type='reset'][disabled]:focus,
|
||||
input[type='reset'][disabled]:hover,
|
||||
input[type='submit'][disabled]:focus,
|
||||
input[type='submit'][disabled]:hover {
|
||||
background-color: #9b4dca;
|
||||
border-color: #9b4dca;
|
||||
}
|
||||
|
||||
.button.button-outline,
|
||||
button.button-outline,
|
||||
input[type='button'].button-outline,
|
||||
input[type='reset'].button-outline,
|
||||
input[type='submit'].button-outline {
|
||||
background-color: transparent;
|
||||
color: #9b4dca;
|
||||
}
|
||||
|
||||
.button.button-outline:focus, .button.button-outline:hover,
|
||||
button.button-outline:focus,
|
||||
button.button-outline:hover,
|
||||
input[type='button'].button-outline:focus,
|
||||
input[type='button'].button-outline:hover,
|
||||
input[type='reset'].button-outline:focus,
|
||||
input[type='reset'].button-outline:hover,
|
||||
input[type='submit'].button-outline:focus,
|
||||
input[type='submit'].button-outline:hover {
|
||||
background-color: transparent;
|
||||
border-color: #606c76;
|
||||
color: #606c76;
|
||||
}
|
||||
|
||||
.button.button-outline[disabled]:focus, .button.button-outline[disabled]:hover,
|
||||
button.button-outline[disabled]:focus,
|
||||
button.button-outline[disabled]:hover,
|
||||
input[type='button'].button-outline[disabled]:focus,
|
||||
input[type='button'].button-outline[disabled]:hover,
|
||||
input[type='reset'].button-outline[disabled]:focus,
|
||||
input[type='reset'].button-outline[disabled]:hover,
|
||||
input[type='submit'].button-outline[disabled]:focus,
|
||||
input[type='submit'].button-outline[disabled]:hover {
|
||||
border-color: inherit;
|
||||
color: #9b4dca;
|
||||
}
|
||||
|
||||
.button.button-clear,
|
||||
button.button-clear,
|
||||
input[type='button'].button-clear,
|
||||
input[type='reset'].button-clear,
|
||||
input[type='submit'].button-clear {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: #9b4dca;
|
||||
}
|
||||
|
||||
.button.button-clear:focus, .button.button-clear:hover,
|
||||
button.button-clear:focus,
|
||||
button.button-clear:hover,
|
||||
input[type='button'].button-clear:focus,
|
||||
input[type='button'].button-clear:hover,
|
||||
input[type='reset'].button-clear:focus,
|
||||
input[type='reset'].button-clear:hover,
|
||||
input[type='submit'].button-clear:focus,
|
||||
input[type='submit'].button-clear:hover {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: #606c76;
|
||||
}
|
||||
|
||||
.button.button-clear[disabled]:focus, .button.button-clear[disabled]:hover,
|
||||
button.button-clear[disabled]:focus,
|
||||
button.button-clear[disabled]:hover,
|
||||
input[type='button'].button-clear[disabled]:focus,
|
||||
input[type='button'].button-clear[disabled]:hover,
|
||||
input[type='reset'].button-clear[disabled]:focus,
|
||||
input[type='reset'].button-clear[disabled]:hover,
|
||||
input[type='submit'].button-clear[disabled]:focus,
|
||||
input[type='submit'].button-clear[disabled]:hover {
|
||||
color: #9b4dca;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f4f5f6;
|
||||
border-radius: .4rem;
|
||||
font-size: 86%;
|
||||
margin: 0 .2rem;
|
||||
padding: .2rem .5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #f4f5f6;
|
||||
border-left: 0.3rem solid #9b4dca;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
border-radius: 0;
|
||||
display: block;
|
||||
padding: 1rem 1.5rem;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 0.1rem solid #f4f5f6;
|
||||
margin: 3.0rem 0;
|
||||
}
|
||||
|
||||
input[type='email'],
|
||||
input[type='number'],
|
||||
input[type='password'],
|
||||
input[type='search'],
|
||||
input[type='tel'],
|
||||
input[type='text'],
|
||||
input[type='url'],
|
||||
textarea,
|
||||
select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border: 0.1rem solid #d1d1d1;
|
||||
border-radius: .4rem;
|
||||
box-shadow: none;
|
||||
box-sizing: inherit;
|
||||
height: 3.8rem;
|
||||
padding: .6rem 1.0rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type='email']:focus,
|
||||
input[type='number']:focus,
|
||||
input[type='password']:focus,
|
||||
input[type='search']:focus,
|
||||
input[type='tel']:focus,
|
||||
input[type='text']:focus,
|
||||
input[type='url']:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #9b4dca;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>') center right no-repeat;
|
||||
padding-right: 3.0rem;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#9b4dca" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>');
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 6.5rem;
|
||||
}
|
||||
|
||||
label,
|
||||
legend {
|
||||
display: block;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.label-inline {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
max-width: 112.0rem;
|
||||
padding: 0 2.0rem;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row.row-no-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.row.row-no-padding > .column {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.row.row-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.row.row-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.row.row-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.row.row-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row.row-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.row.row-baseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.row .column {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
margin-left: 0;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-10 {
|
||||
margin-left: 10%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-20 {
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-25 {
|
||||
margin-left: 25%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-33, .row .column.column-offset-34 {
|
||||
margin-left: 33.3333%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-50 {
|
||||
margin-left: 50%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-66, .row .column.column-offset-67 {
|
||||
margin-left: 66.6666%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-75 {
|
||||
margin-left: 75%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-80 {
|
||||
margin-left: 80%;
|
||||
}
|
||||
|
||||
.row .column.column-offset-90 {
|
||||
margin-left: 90%;
|
||||
}
|
||||
|
||||
.row .column.column-10 {
|
||||
flex: 0 0 10%;
|
||||
max-width: 10%;
|
||||
}
|
||||
|
||||
.row .column.column-20 {
|
||||
flex: 0 0 20%;
|
||||
max-width: 20%;
|
||||
}
|
||||
|
||||
.row .column.column-25 {
|
||||
flex: 0 0 25%;
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
.row .column.column-33, .row .column.column-34 {
|
||||
flex: 0 0 33.3333%;
|
||||
max-width: 33.3333%;
|
||||
}
|
||||
|
||||
.row .column.column-40 {
|
||||
flex: 0 0 40%;
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.row .column.column-50 {
|
||||
flex: 0 0 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.row .column.column-60 {
|
||||
flex: 0 0 60%;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.row .column.column-66, .row .column.column-67 {
|
||||
flex: 0 0 66.6666%;
|
||||
max-width: 66.6666%;
|
||||
}
|
||||
|
||||
.row .column.column-75 {
|
||||
flex: 0 0 75%;
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.row .column.column-80 {
|
||||
flex: 0 0 80%;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.row .column.column-90 {
|
||||
flex: 0 0 90%;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.row .column .column-top {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.row .column .column-bottom {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.row .column .column-center {
|
||||
-ms-grid-row-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
.row {
|
||||
flex-direction: row;
|
||||
margin-left: -1.0rem;
|
||||
width: calc(100% + 2.0rem);
|
||||
}
|
||||
.row .column {
|
||||
margin-bottom: inherit;
|
||||
padding: 0 1.0rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #9b4dca;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:focus, a:hover {
|
||||
color: #606c76;
|
||||
}
|
||||
|
||||
dl,
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
margin-top: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
dl dl,
|
||||
dl ol,
|
||||
dl ul,
|
||||
ol dl,
|
||||
ol ol,
|
||||
ol ul,
|
||||
ul dl,
|
||||
ul ol,
|
||||
ul ul {
|
||||
font-size: 90%;
|
||||
margin: 1.5rem 0 1.5rem 3.0rem;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal inside;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: circle inside;
|
||||
}
|
||||
|
||||
.button,
|
||||
button,
|
||||
dd,
|
||||
dt,
|
||||
li {
|
||||
margin-bottom: 1.0rem;
|
||||
}
|
||||
|
||||
fieldset,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
figure,
|
||||
form,
|
||||
ol,
|
||||
p,
|
||||
pre,
|
||||
table,
|
||||
ul {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border-bottom: 0.1rem solid #e1e1e1;
|
||||
padding: 1.2rem 1.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td:first-child,
|
||||
th:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
td:last-child,
|
||||
th:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 300;
|
||||
letter-spacing: -.1rem;
|
||||
margin-bottom: 2.0rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4.6rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 3.6rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2.8rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 2.2rem;
|
||||
letter-spacing: -.08rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.8rem;
|
||||
letter-spacing: -.05rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1.6rem;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=milligram.css.map */
|
@ -1,12 +1,13 @@
|
||||
* {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#wineList {
|
||||
overflow-y: scroll;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.leftArea {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
|
@ -1,63 +1,70 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Cellar</title>
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
<title>Cellar</title>
|
||||
<link rel="stylesheet" href="css/milligram.css" />
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<input type="text" id="searchKey"/>
|
||||
<button id="btnSearch">Search</button>
|
||||
<button id="btnAdd">New Wine</button>
|
||||
</div>
|
||||
<div class="header">
|
||||
<input type="text" id="searchKey" />
|
||||
<button class="button" id="btnSearch">Search</button>
|
||||
<button class="button" id="btnAdd">New Wine</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="leftArea">
|
||||
<ul id="wineList"></ul>
|
||||
</div>
|
||||
<div class="leftArea">
|
||||
<ul id="wineList"></ul>
|
||||
</div>
|
||||
|
||||
<form id="wineForm">
|
||||
<form id="wineForm">
|
||||
|
||||
<div class="mainArea">
|
||||
<div class="mainArea">
|
||||
|
||||
<label>Id:</label>
|
||||
<input id="wineId" name="id" type="text" style="width: 50px" disabled />
|
||||
<label>Id:</label>
|
||||
<input id="wineId" name="id" type="text" style="width: 50px" disabled />
|
||||
|
||||
<label>Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
<label>Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
|
||||
<label>Grapes:</label>
|
||||
<input type="text" id="grapes" name="grapes"/>
|
||||
<label>Grapes:</label>
|
||||
<input type="text" id="grapes" name="grapes" />
|
||||
|
||||
<label>Country:</label>
|
||||
<input type="text" id="country" name="country"/>
|
||||
<label>Country:</label>
|
||||
<input type="text" id="country" name="country" />
|
||||
|
||||
<label>Region:</label>
|
||||
<input type="text" id="region" name="region"/>
|
||||
<label>Region:</label>
|
||||
<input type="text" id="region" name="region" />
|
||||
|
||||
<label>Year:</label>
|
||||
<select id="year" name="year"><option value=""></option></select>
|
||||
<div class="padding02">
|
||||
<button id="btnSave">Save</button>
|
||||
<button id="btnDelete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<label>Year:</label>
|
||||
<select id="year" name="year">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
<div class="padding02">
|
||||
<button class="button" id="btnSave">Save</button>
|
||||
<button class="button" id="btnDelete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rightArea">
|
||||
<div class="rightArea">
|
||||
|
||||
<img id="pic" height="300"/>
|
||||
<img id="pic" height="300" />
|
||||
|
||||
<label>Notes:</label>
|
||||
<textarea id="description" name="description"></textarea>
|
||||
</div>
|
||||
<label>Notes:</label>
|
||||
<textarea id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</form>
|
||||
|
||||
<script src="js/jquery-1.7.1.min.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/jquery-1.7.1.min.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
|
||||
<div class="footer textcenter padding02">The client code of this demo is slightly based on <a target="_blank" href="http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/">"RESTful services with jQuery, PHP and the Slim Framework"</a></div>
|
||||
<div class="footer textcenter padding02">The client code of this demo is slightly based on
|
||||
<a target="_blank" href="http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/">"RESTful services with jQuery, PHP and the Slim Framework"</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -55,10 +55,11 @@ implementation
|
||||
|
||||
uses
|
||||
System.SysUtils, System.Classes, System.IOUtils,
|
||||
WinesBO, MVCFramework.Serializer.Commons;
|
||||
WinesBO, MVCFramework.Serializer.Commons, MVCFramework.Logger;
|
||||
|
||||
procedure TWineCellarApp.FindWines(ctx: TWebContext);
|
||||
begin
|
||||
Log.Debug('','MYTAG');
|
||||
Render(dm.FindWines(ctx.Request.Params['value']));
|
||||
end;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{61ADE231-72F2-4E11-8EDD-62C5AFEF0463}</ProjectGuid>
|
||||
<ProjectVersion>18.3</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>mod_dmvc.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -3,11 +3,27 @@ unit MainDM;
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option,
|
||||
FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
|
||||
FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.FB, Data.DB,
|
||||
FireDAC.Comp.Client, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
|
||||
FireDAC.DApt, FireDAC.Comp.DataSet, FireDAC.Phys.FBDef, FireDAC.VCLUI.Wait;
|
||||
System.SysUtils,
|
||||
System.Classes,
|
||||
FireDAC.Stan.Intf,
|
||||
FireDAC.Stan.Option,
|
||||
FireDAC.Stan.Error,
|
||||
FireDAC.UI.Intf,
|
||||
FireDAC.Phys.Intf,
|
||||
FireDAC.Stan.Def,
|
||||
FireDAC.Stan.Pool,
|
||||
FireDAC.Stan.Async,
|
||||
FireDAC.Phys,
|
||||
FireDAC.Phys.FB,
|
||||
Data.DB,
|
||||
FireDAC.Comp.Client,
|
||||
FireDAC.Stan.Param,
|
||||
FireDAC.DatS,
|
||||
FireDAC.DApt.Intf,
|
||||
FireDAC.DApt,
|
||||
FireDAC.Comp.DataSet,
|
||||
FireDAC.Phys.FBDef,
|
||||
FireDAC.VCLUI.Wait;
|
||||
|
||||
type
|
||||
TdmMain = class(TDataModule)
|
||||
@ -28,10 +44,13 @@ implementation
|
||||
|
||||
procedure TdmMain.ConnectionBeforeConnect(Sender: TObject);
|
||||
begin
|
||||
// currently, this demo uses firebird 2.5
|
||||
{$IFNDEF WINDOWSSERVICE}
|
||||
// if you want to use firebird 2.5, you can use the file ORDERSMANAGER_FB25.FDB
|
||||
Connection.Params.Values['Database'] := '..\..\data\ORDERSMANAGER_FB30.FDB';
|
||||
// Connection.Params.Values['Database'] := '..\..\data\ORDERSMANAGER_FB25.FDB';
|
||||
{$ELSE}
|
||||
Connection.Params.Values['Database'] := 'C:\DEV\dmvcframework\samples\data\ORDERSMANAGER_FB30.FDB';
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -6,7 +6,6 @@ object WebModule1: TWebModule1
|
||||
Default = True
|
||||
Name = 'DefaultHandler'
|
||||
PathInfo = '/'
|
||||
OnAction = WebModule1DefaultHandlerAction
|
||||
end>
|
||||
Height = 230
|
||||
Width = 415
|
||||
|
@ -6,8 +6,6 @@ uses System.SysUtils, System.Classes, Web.HTTPApp, mvcframework;
|
||||
|
||||
type
|
||||
TWebModule1 = class(TWebModule)
|
||||
procedure WebModule1DefaultHandlerAction(Sender: TObject;
|
||||
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
|
||||
procedure WebModuleCreate(Sender: TObject);
|
||||
private
|
||||
FEngine: TMVCEngine;
|
||||
@ -26,22 +24,12 @@ uses Controllers.Articles, MVCFramework.Middleware.CORS, MVCFramework.Middleware
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
|
||||
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
|
||||
begin
|
||||
Response.Content :=
|
||||
'<html>' +
|
||||
'<head><title>Web Server Application</title></head>' +
|
||||
'<body>Web Server Application</body>' +
|
||||
'</html>';
|
||||
end;
|
||||
|
||||
procedure TWebModule1.WebModuleCreate(Sender: TObject);
|
||||
begin
|
||||
FEngine := TMVCEngine.Create(self);
|
||||
FEngine.AddController(TArticlesController);
|
||||
FEngine.AddMiddleware(TCORSMiddleware.Create);
|
||||
FEngine.AddMiddleware(TCompressionMiddleware.Create);
|
||||
FEngine.AddMiddleware(TCompressionMiddleware.Create(256));
|
||||
|
||||
end;
|
||||
|
||||
|
@ -82,7 +82,6 @@ object Form5: TForm5
|
||||
Caption = 'Get a protected resource'
|
||||
TabOrder = 0
|
||||
OnClick = btnGetClick
|
||||
ExplicitTop = 2
|
||||
end
|
||||
object btnLOGIN: TButton
|
||||
AlignWithMargins = True
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{7B54055A-5749-4136-9FE2-35FDBEEA874C}</ProjectGuid>
|
||||
<ProjectVersion>18.2</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>JWTServer.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -27,7 +27,6 @@ implementation
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
uses
|
||||
AppControllerU,
|
||||
System.Generics.Collections,
|
||||
@ -45,11 +44,12 @@ begin
|
||||
JWT.Claims.Issuer := 'Delphi MVC Framework JWT Middleware Sample';
|
||||
JWT.Claims.NotBefore := Now - OneMinute * 5; // valid since 5 minutes ago
|
||||
JWT.Claims.IssuedAt := Now;
|
||||
JWT.Claims.ExpirationTime := Now + OneSecond * 30;
|
||||
JWT.CustomClaims['mycustomvalue'] := 'hello there';
|
||||
// Here we dont use a fixed ExpirationTime but a LiveValidityWindowInSeconds
|
||||
// to make the ExpirationTime dynamic, incrementing the
|
||||
// ExpirationTime by LiveValidityWindowInSeconds seconds at each request
|
||||
JWT.LiveValidityWindowInSeconds := 5; // 60 * 60; // 1 hour
|
||||
JWT.LiveValidityWindowInSeconds := 10; // 60 * 60; // 1 hour
|
||||
end;
|
||||
|
||||
MVC := TMVCEngine.Create(Self);
|
||||
@ -57,17 +57,9 @@ begin
|
||||
MVC.Config[TMVCConfigKey.SessionTimeout] := '30';
|
||||
MVC.Config[TMVCConfigKey.DefaultContentType] := 'text/html';
|
||||
MVC.AddController(TApp1MainController).AddController(TAdminController)
|
||||
.AddMiddleware(TMVCJWTAuthenticationMiddleware.Create(
|
||||
TAuthenticationSample.Create,
|
||||
lClaimsSetup,
|
||||
'mys3cr37',
|
||||
'/login',
|
||||
[
|
||||
TJWTCheckableClaim.ExpirationTime,
|
||||
TJWTCheckableClaim.NotBefore,
|
||||
TJWTCheckableClaim.IssuedAt
|
||||
],
|
||||
0 // just for test, Leeway seconds is zero.
|
||||
.AddMiddleware(TMVCJWTAuthenticationMiddleware.Create(TAuthenticationSample.Create, lClaimsSetup, 'mys3cr37',
|
||||
'/login', [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore, TJWTCheckableClaim.IssuedAt], 0
|
||||
// just for test, Leeway seconds is zero.
|
||||
));
|
||||
end;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E7317702-64D3-4A65-8734-030F3AE3DBBC}</ProjectGuid>
|
||||
<ProjectVersion>18.2</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>JWTClient.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{0388D146-2B8B-427B-AEDD-EFD5F51D3139}</ProjectGuid>
|
||||
<ProjectVersion>18.2</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>MiddlewareSamples.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -4,7 +4,6 @@ program OutputCacheWithRedis;
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
Winapi.Windows,
|
||||
IdHTTPWebBrokerBridge,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
@ -20,9 +19,6 @@ uses
|
||||
|
||||
procedure RunServer(APort: Integer);
|
||||
var
|
||||
LInputRecord: TInputRecord;
|
||||
LEvent: DWord;
|
||||
LHandle: THandle;
|
||||
LServer: TIdHTTPWebBrokerBridge;
|
||||
begin
|
||||
Writeln(Format('Starting "OutputCacheWithRedis" HTTP Server or port %d', [APort]));
|
||||
@ -32,16 +28,8 @@ begin
|
||||
LServer.ListenQueue := 200;
|
||||
LServer.MaxConnections := 5000;
|
||||
LServer.Active := True;
|
||||
Writeln('Press ESC to stop the server');
|
||||
LHandle := GetStdHandle(STD_INPUT_HANDLE);
|
||||
while True do
|
||||
begin
|
||||
Win32Check(ReadConsoleInput(LHandle, LInputRecord, 1, LEvent));
|
||||
if (LInputRecord.EventType = KEY_EVENT) and
|
||||
LInputRecord.Event.KeyEvent.bKeyDown and
|
||||
(LInputRecord.Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE) then
|
||||
break;
|
||||
end;
|
||||
Writeln('Press RETURN to stop the server');
|
||||
ReadLn;
|
||||
finally
|
||||
LServer.Free;
|
||||
end;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{BE3A3D14-17E0-45C1-BD21-4710DE4CBCC2}</ProjectGuid>
|
||||
<ProjectVersion>18.2</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>OutputCacheWithRedis.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -64,8 +64,6 @@ type
|
||||
{ Public declarations }
|
||||
end;
|
||||
|
||||
var
|
||||
MyDataModule: TMyDataModule;
|
||||
|
||||
implementation
|
||||
|
||||
|
@ -229,7 +229,7 @@ begin
|
||||
lDM := TMyDataModule.Create(nil);
|
||||
try
|
||||
lDM.qryCustomers.Open;
|
||||
Render(lDM.qryCustomers);
|
||||
Render(lDM.qryCustomers, False);
|
||||
finally
|
||||
lDM.Free;
|
||||
end;
|
||||
|
@ -404,37 +404,37 @@
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txaPackage" Configuration="Debug" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txvpck" Configuration="Debug" Class="ProjectFile">
|
||||
<Platform Name="Linux64">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\RenderSampleControllerU\default.txaPackage" Configuration="Release" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders1\default.txvpck" Configuration="Release" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txvpck" Configuration="Release" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="bin\renders.exe" Configuration="Release" Class="ProjectOutput">
|
||||
<Platform Name="Win32">
|
||||
<RemoteName>renders.exe</RemoteName>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txvpck" Configuration="Debug" Class="ProjectFile">
|
||||
<Platform Name="Linux64">
|
||||
<DeployFile LocalName="ModelSupport_renders\renders1\default.txvpck" Configuration="Release" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txaPackage" Configuration="Debug" Class="ProjectFile">
|
||||
<DeployFile LocalName="ModelSupport_renders\renders\default.txvpck" Configuration="Release" Class="ProjectFile">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>.\</RemoteDir>
|
||||
<Overwrite>true</Overwrite>
|
||||
|
@ -11,7 +11,7 @@ type
|
||||
TRoutingSampleController = class(TMVCController)
|
||||
public
|
||||
[MVCPath('/')]
|
||||
procedure Index(CTX: TWebContext);
|
||||
procedure Index;
|
||||
|
||||
{ This action requires that the ACCEPT header is text/plain to be invocated }
|
||||
[MVCHTTPMethod([httpGet])]
|
||||
@ -57,7 +57,7 @@ begin
|
||||
lPerson := Context.Request.BodyAs<TPerson>;
|
||||
lPerson.Validate;
|
||||
// SavePerson(lPerson);
|
||||
Render(201, 'Person created');
|
||||
Render(HTTP_STATUS.Created, 'Person created');
|
||||
end;
|
||||
|
||||
procedure TRoutingSampleController.DeletePerson(const id: Integer);
|
||||
@ -83,7 +83,7 @@ begin
|
||||
Render(P);
|
||||
end;
|
||||
|
||||
procedure TRoutingSampleController.Index(CTX: TWebContext);
|
||||
procedure TRoutingSampleController.Index;
|
||||
begin
|
||||
Render('This is the root path');
|
||||
end;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{F9CBCE21-869A-478F-992C-88FCAC97BC8B}</ProjectGuid>
|
||||
<ProjectVersion>18.2</ProjectVersion>
|
||||
<ProjectVersion>18.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>SessionSample.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
|
@ -1,12 +1,13 @@
|
||||
* {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#wineList {
|
||||
overflow-y: scroll;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.leftArea {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
|
@ -1,63 +1,70 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Cellar</title>
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
<title>Cellar</title>
|
||||
<link rel="stylesheet" href="css/milligram.css" />
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<input type="text" id="searchKey"/>
|
||||
<button id="btnSearch">Search</button>
|
||||
<button id="btnAdd">New Wine</button>
|
||||
</div>
|
||||
<div class="header">
|
||||
<input type="text" id="searchKey" />
|
||||
<button class="button" id="btnSearch">Search</button>
|
||||
<button class="button" id="btnAdd">New Wine</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="leftArea">
|
||||
<ul id="wineList"></ul>
|
||||
</div>
|
||||
<div class="leftArea">
|
||||
<ul id="wineList"></ul>
|
||||
</div>
|
||||
|
||||
<form id="wineForm">
|
||||
<form id="wineForm">
|
||||
|
||||
<div class="mainArea">
|
||||
<div class="mainArea">
|
||||
|
||||
<label>Id:</label>
|
||||
<input id="wineId" name="id" type="text" style="width: 50px" disabled />
|
||||
<label>Id:</label>
|
||||
<input id="wineId" name="id" type="text" style="width: 50px" disabled />
|
||||
|
||||
<label>Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
<label>Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
|
||||
<label>Grapes:</label>
|
||||
<input type="text" id="grapes" name="grapes"/>
|
||||
<label>Grapes:</label>
|
||||
<input type="text" id="grapes" name="grapes" />
|
||||
|
||||
<label>Country:</label>
|
||||
<input type="text" id="country" name="country"/>
|
||||
<label>Country:</label>
|
||||
<input type="text" id="country" name="country" />
|
||||
|
||||
<label>Region:</label>
|
||||
<input type="text" id="region" name="region"/>
|
||||
<label>Region:</label>
|
||||
<input type="text" id="region" name="region" />
|
||||
|
||||
<label>Year:</label>
|
||||
<select id="year" name="year"><option value=""></option></select>
|
||||
<div class="padding02">
|
||||
<button id="btnSave">Save</button>
|
||||
<button id="btnDelete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<label>Year:</label>
|
||||
<select id="year" name="year">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
<div class="padding02">
|
||||
<button class="button" id="btnSave">Save</button>
|
||||
<button class="button" id="btnDelete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rightArea">
|
||||
<div class="rightArea">
|
||||
|
||||
<img id="pic" height="300"/>
|
||||
<img id="pic" height="300" />
|
||||
|
||||
<label>Notes:</label>
|
||||
<textarea id="description" name="description"></textarea>
|
||||
</div>
|
||||
<label>Notes:</label>
|
||||
<textarea id="description" name="description"></textarea>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</form>
|
||||
|
||||
<script src="js/jquery-1.7.1.min.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/jquery-1.7.1.min.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
|
||||
<div class="footer textcenter padding02">The client code of this demo is slightly based on <a target="_blank" href="http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/">"RESTful services with jQuery, PHP and the Slim Framework"</a></div>
|
||||
<div class="footer textcenter padding02">The client code of this demo is slightly based on
|
||||
<a target="_blank" href="http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/">"RESTful services with jQuery, PHP and the Slim Framework"</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -208,8 +208,9 @@ type
|
||||
property LeewaySeconds: Cardinal read FLeewaySeconds;
|
||||
property RegClaimsToChecks: TJWTCheckableClaims read FRegClaimsToChecks write SetChecks;
|
||||
/// <summary>
|
||||
/// Use LiveValidityWindowInSeconds to make the ExpirationTime dynamic at each request,
|
||||
/// incrementing the ExpirationTime by LiveValidityWindowInSeconds seconds at each request
|
||||
/// Use LiveValidityWindowInSeconds to make the ExpirationTime dynamic at each request.
|
||||
/// ExpirationTime will be incremented by LiveValidityWindowInSeconds seconds automatically
|
||||
/// if the remaining seconds are less than the LiveValidityWindowInSeconds.
|
||||
/// </summary>
|
||||
property LiveValidityWindowInSeconds: Cardinal read GetLiveValidityWindowInSeconds write SetLiveValidityWindowInSeconds;
|
||||
end;
|
||||
|
@ -50,67 +50,40 @@ type
|
||||
FLeewaySeconds: Cardinal;
|
||||
FLoginURLSegment: string;
|
||||
protected
|
||||
procedure InternalRender(
|
||||
AJSONValue: TJSONValue;
|
||||
AContentType: string;
|
||||
AContentEncoding: string;
|
||||
AContext: TWebContext;
|
||||
AInstanceOwner: Boolean = True
|
||||
);
|
||||
function NeedsToBeExtended(const JWTValue: TJWT): Boolean;
|
||||
procedure ExtendExpirationTime(const JWTValue: TJWT);
|
||||
procedure InternalRender(AJSONValue: TJSONValue; AContentType: string; AContentEncoding: string;
|
||||
AContext: TWebContext; AInstanceOwner: Boolean = True);
|
||||
|
||||
procedure RenderError(
|
||||
const AErrorCode: UInt16;
|
||||
const AErrorMessage: string;
|
||||
const AContext: TWebContext;
|
||||
const AErrorClassName: string = ''
|
||||
);
|
||||
procedure RenderError(const AErrorCode: UInt16; const AErrorMessage: string; const AContext: TWebContext;
|
||||
const AErrorClassName: string = '');
|
||||
|
||||
procedure OnBeforeRouting(
|
||||
AContext: TWebContext;
|
||||
var AHandled: Boolean
|
||||
);
|
||||
procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
|
||||
|
||||
procedure OnBeforeControllerAction(
|
||||
AContext: TWebContext;
|
||||
const AControllerQualifiedClassName: string;
|
||||
const AActionName: string;
|
||||
var AHandled: Boolean
|
||||
);
|
||||
procedure OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName: string;
|
||||
const AActionName: string; var AHandled: Boolean);
|
||||
|
||||
procedure OnAfterControllerAction(
|
||||
AContext: TWebContext;
|
||||
const AActionName: string;
|
||||
const AHandled: Boolean
|
||||
);
|
||||
procedure OnAfterControllerAction(AContext: TWebContext; const AActionName: string; const AHandled: Boolean);
|
||||
public
|
||||
constructor Create(AAuthenticationHandler: IMVCAuthenticationHandler;
|
||||
AConfigClaims: TJWTClaimsSetup;
|
||||
ASecret: string = 'D3lph1MVCFram3w0rk';
|
||||
ALoginURLSegment: string = '/login';
|
||||
AClaimsToCheck: TJWTCheckableClaims = [
|
||||
TJWTCheckableClaim.ExpirationTime,
|
||||
TJWTCheckableClaim.NotBefore,
|
||||
TJWTCheckableClaim.IssuedAt
|
||||
];
|
||||
ALeewaySeconds: Cardinal = 300); virtual;
|
||||
constructor Create(AAuthenticationHandler: IMVCAuthenticationHandler; AConfigClaims: TJWTClaimsSetup;
|
||||
ASecret: string = 'D3lph1MVCFram3w0rk'; ALoginURLSegment: string = '/login';
|
||||
AClaimsToCheck: TJWTCheckableClaims = [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
||||
TJWTCheckableClaim.IssuedAt]; ALeewaySeconds: Cardinal = 300); virtual;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses System.NetEncoding, System.DateUtils;
|
||||
uses
|
||||
System.NetEncoding,
|
||||
System.DateUtils,
|
||||
System.Math;
|
||||
|
||||
{ TMVCJWTAuthenticationMiddleware }
|
||||
|
||||
constructor TMVCJWTAuthenticationMiddleware.Create(AAuthenticationHandler: IMVCAuthenticationHandler;
|
||||
AConfigClaims: TJWTClaimsSetup;
|
||||
ASecret: string = 'D3lph1MVCFram3w0rk';
|
||||
ALoginURLSegment: string = '/login';
|
||||
AClaimsToCheck: TJWTCheckableClaims = [
|
||||
TJWTCheckableClaim.ExpirationTime,
|
||||
TJWTCheckableClaim.NotBefore,
|
||||
TJWTCheckableClaim.IssuedAt
|
||||
];
|
||||
ALeewaySeconds: Cardinal = 300);
|
||||
AConfigClaims: TJWTClaimsSetup; ASecret: string = 'D3lph1MVCFram3w0rk'; ALoginURLSegment: string = '/login';
|
||||
AClaimsToCheck: TJWTCheckableClaims = [TJWTCheckableClaim.ExpirationTime, TJWTCheckableClaim.NotBefore,
|
||||
TJWTCheckableClaim.IssuedAt]; ALeewaySeconds: Cardinal = 300);
|
||||
begin
|
||||
inherited Create;
|
||||
FAuthenticationHandler := AAuthenticationHandler;
|
||||
@ -121,8 +94,13 @@ begin
|
||||
FLeewaySeconds := ALeewaySeconds;
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.InternalRender(
|
||||
AJSONValue: TJSONValue; AContentType, AContentEncoding: string;
|
||||
procedure TMVCJWTAuthenticationMiddleware.ExtendExpirationTime(const JWTValue: TJWT);
|
||||
begin
|
||||
JWTValue.Claims.ExpirationTime := Max(JWTValue.Claims.ExpirationTime, Now) +
|
||||
(JWTValue.LeewaySeconds + JWTValue.LiveValidityWindowInSeconds) * OneSecond;
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.InternalRender(AJSONValue: TJSONValue; AContentType, AContentEncoding: string;
|
||||
AContext: TWebContext; AInstanceOwner: Boolean);
|
||||
var
|
||||
Encoding: TEncoding;
|
||||
@ -135,9 +113,8 @@ begin
|
||||
|
||||
Encoding := TEncoding.GetEncoding(AContentEncoding);
|
||||
try
|
||||
AContext.Response.SetContentStream(
|
||||
TBytesStream.Create(TEncoding.Convert(TEncoding.Default, Encoding, TEncoding.Default.GetBytes(JValue))),
|
||||
ContentType);
|
||||
AContext.Response.SetContentStream(TBytesStream.Create(TEncoding.Convert(TEncoding.Default, Encoding,
|
||||
TEncoding.Default.GetBytes(JValue))), ContentType);
|
||||
finally
|
||||
Encoding.Free;
|
||||
end;
|
||||
@ -146,16 +123,28 @@ begin
|
||||
FreeAndNil(AJSONValue)
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnAfterControllerAction(
|
||||
AContext: TWebContext; const AActionName: string;
|
||||
function TMVCJWTAuthenticationMiddleware.NeedsToBeExtended(const JWTValue: TJWT): Boolean;
|
||||
var
|
||||
lWillExpireIn: Int64;
|
||||
begin
|
||||
lWillExpireIn := SecondsBetween(Now, JWTValue.Claims.ExpirationTime);
|
||||
Result := lWillExpireIn <= JWTValue.LiveValidityWindowInSeconds;
|
||||
// Log.Debug('--------------------------', 'EXPIRE');
|
||||
// Log.DebugFmt('Now : %s', [TimeToStr(Now)], 'EXPIRE');
|
||||
// Log.DebugFmt('ExpirationTime : %s', [TimeToStr(JWTValue.Claims.ExpirationTime)], 'EXPIRE');
|
||||
// Log.DebugFmt('WillExpireIn : %d', [lWillExpireIn], 'EXPIRE');
|
||||
// Log.DebugFmt('LVW : %d', [JWTValue.LiveValidityWindowInSeconds], 'EXPIRE');
|
||||
// Log.DebugFmt('NeedsToBeExtened: %s', [BoolToStr(Result, True)], 'EXPIRE');
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnAfterControllerAction(AContext: TWebContext; const AActionName: string;
|
||||
const AHandled: Boolean);
|
||||
begin
|
||||
// Implement as needed
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnBeforeControllerAction(
|
||||
AContext: TWebContext; const AControllerQualifiedClassName,
|
||||
AActionName: string; var AHandled: Boolean);
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnBeforeControllerAction(AContext: TWebContext;
|
||||
const AControllerQualifiedClassName, AActionName: string; var AHandled: Boolean);
|
||||
var
|
||||
AuthRequired: Boolean;
|
||||
IsAuthorized: Boolean;
|
||||
@ -223,14 +212,19 @@ begin
|
||||
AContext.LoggedUser.LoggedSince := JWTValue.Claims.IssuedAt;
|
||||
AContext.LoggedUser.CustomData := JWTValue.CustomClaims.AsCustomData;
|
||||
|
||||
FAuthenticationHandler.OnAuthorization(AContext.LoggedUser.Roles, AControllerQualifiedClassName, AActionName, IsAuthorized);
|
||||
FAuthenticationHandler.OnAuthorization(AContext.LoggedUser.Roles, AControllerQualifiedClassName, AActionName,
|
||||
IsAuthorized);
|
||||
|
||||
if IsAuthorized then
|
||||
begin
|
||||
if JWTValue.LiveValidityWindowInSeconds > 0 then
|
||||
begin
|
||||
JWTValue.Claims.ExpirationTime := Now + JWTValue.LiveValidityWindowInSeconds * OneSecond;
|
||||
AContext.Response.SetCustomHeader('Authentication', 'bearer ' + JWTValue.GetToken);
|
||||
if NeedsToBeExtended(JWTValue) then
|
||||
begin
|
||||
ExtendExpirationTime(JWTValue);
|
||||
// .Claims.ExpirationTime := Now + JWTValue.LiveValidityWindowInSeconds * OneSecond;
|
||||
AContext.Response.SetCustomHeader('Authentication', 'bearer ' + JWTValue.GetToken);
|
||||
end;
|
||||
end;
|
||||
AHandled := False
|
||||
end
|
||||
@ -245,8 +239,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting(
|
||||
AContext: TWebContext; var AHandled: Boolean);
|
||||
procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
|
||||
var
|
||||
UserName: string;
|
||||
Password: string;
|
||||
@ -284,7 +277,8 @@ begin
|
||||
|
||||
// these claims are mandatory and managed by the middleware
|
||||
if not JWTValue.CustomClaims['username'].IsEmpty then
|
||||
raise EMVCJWTException.Create('Custom claim "username" is reserved and cannot be modified in the JWT setup');
|
||||
raise EMVCJWTException.Create
|
||||
('Custom claim "username" is reserved and cannot be modified in the JWT setup');
|
||||
|
||||
if not JWTValue.CustomClaims['roles'].IsEmpty then
|
||||
raise EMVCJWTException.Create('Custom claim "roles" is reserved and cannot be modified in the JWT setup');
|
||||
@ -294,7 +288,10 @@ begin
|
||||
|
||||
if JWTValue.LiveValidityWindowInSeconds > 0 then
|
||||
begin
|
||||
JWTValue.Claims.ExpirationTime := Now + (JWTValue.LeewaySeconds + JWTValue.LiveValidityWindowInSeconds) * OneSecond;
|
||||
if NeedsToBeExtended(JWTValue) then
|
||||
begin
|
||||
ExtendExpirationTime(JWTValue);
|
||||
end;
|
||||
end;
|
||||
|
||||
// setup the current logged user from the JWT
|
||||
@ -315,12 +312,8 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
InternalRender(
|
||||
TJSONObject.Create(TJSONPair.Create('token', JWTValue.GetToken)),
|
||||
TMVCMediaType.APPLICATION_JSON,
|
||||
TMVCConstants.DEFAULT_CONTENT_CHARSET,
|
||||
AContext
|
||||
);
|
||||
InternalRender(TJSONObject.Create(TJSONPair.Create('token', JWTValue.GetToken)),
|
||||
TMVCMediaType.APPLICATION_JSON, TMVCConstants.DEFAULT_CONTENT_CHARSET, AContext);
|
||||
AHandled := True;
|
||||
finally
|
||||
JWTValue.Free;
|
||||
@ -340,9 +333,8 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCJWTAuthenticationMiddleware.RenderError(const AErrorCode: UInt16;
|
||||
const AErrorMessage: string; const AContext: TWebContext;
|
||||
const AErrorClassName: string);
|
||||
procedure TMVCJWTAuthenticationMiddleware.RenderError(const AErrorCode: UInt16; const AErrorMessage: string;
|
||||
const AContext: TWebContext; const AErrorClassName: string);
|
||||
var
|
||||
Jo: TJSONObject;
|
||||
Status: string;
|
||||
|
Loading…
Reference in New Issue
Block a user