diff --git a/samples/apachemodule/Apache24/htdocs/css/milligram.css b/samples/apachemodule/Apache24/htdocs/css/milligram.css new file mode 100644 index 000000000..d253355e5 --- /dev/null +++ b/samples/apachemodule/Apache24/htdocs/css/milligram.css @@ -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,') center right no-repeat; + padding-right: 3.0rem; +} + +select:focus { + background-image: url('data:image/svg+xml;utf8,'); +} + +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 */ \ No newline at end of file diff --git a/samples/apachemodule/Apache24/htdocs/css/styles.css b/samples/apachemodule/Apache24/htdocs/css/styles.css index 6971a0c2b..4c56f8b3d 100644 --- a/samples/apachemodule/Apache24/htdocs/css/styles.css +++ b/samples/apachemodule/Apache24/htdocs/css/styles.css @@ -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; diff --git a/samples/apachemodule/Apache24/htdocs/index.html b/samples/apachemodule/Apache24/htdocs/index.html index 7675d93b0..2d908d266 100644 --- a/samples/apachemodule/Apache24/htdocs/index.html +++ b/samples/apachemodule/Apache24/htdocs/index.html @@ -1,63 +1,70 @@ + -Cellar - + Cellar + + -
- - - -
+
+ + + +
-
- -
+
+ +
-
+ -
+
- - + + - - + + - - + + - - + + - - + + - - -
- - -
-
+ + +
+ + +
+
-
+
- + - - -
+ + +
-
+ - - + + - + + \ No newline at end of file diff --git a/samples/apachemodule/WineCellarAppControllerU.pas b/samples/apachemodule/WineCellarAppControllerU.pas index 9e0bf7eaa..9fcb4e866 100644 --- a/samples/apachemodule/WineCellarAppControllerU.pas +++ b/samples/apachemodule/WineCellarAppControllerU.pas @@ -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; diff --git a/samples/apachemodule/mod_dmvc.dproj b/samples/apachemodule/mod_dmvc.dproj index fa9d9c7ae..f4d8ab3ed 100644 --- a/samples/apachemodule/mod_dmvc.dproj +++ b/samples/apachemodule/mod_dmvc.dproj @@ -1,7 +1,7 @@  {61ADE231-72F2-4E11-8EDD-62C5AFEF0463} - 18.3 + 18.4 VCL mod_dmvc.dpr True diff --git a/samples/articles_crud_server/MainDM.pas b/samples/articles_crud_server/MainDM.pas index 8c371fe13..20b0fc648 100644 --- a/samples/articles_crud_server/MainDM.pas +++ b/samples/articles_crud_server/MainDM.pas @@ -3,11 +3,27 @@ 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. diff --git a/samples/articles_crud_server/WebModuleUnit1.dfm b/samples/articles_crud_server/WebModuleUnit1.dfm index 417085809..b43b8558a 100644 --- a/samples/articles_crud_server/WebModuleUnit1.dfm +++ b/samples/articles_crud_server/WebModuleUnit1.dfm @@ -6,7 +6,6 @@ object WebModule1: TWebModule1 Default = True Name = 'DefaultHandler' PathInfo = '/' - OnAction = WebModule1DefaultHandlerAction end> Height = 230 Width = 415 diff --git a/samples/articles_crud_server/WebModuleUnit1.pas b/samples/articles_crud_server/WebModuleUnit1.pas index 46e2fe027..0d153074e 100644 --- a/samples/articles_crud_server/WebModuleUnit1.pas +++ b/samples/articles_crud_server/WebModuleUnit1.pas @@ -6,8 +6,6 @@ interface 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 @@ implementation {$R *.dfm} -procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject; - Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); -begin - Response.Content := - '' + - 'Web Server Application' + - 'Web Server Application' + - ''; -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; diff --git a/samples/jsonwebtoken/vclclient/MainClientFormU.dfm b/samples/jsonwebtoken/vclclient/MainClientFormU.dfm index 82b489d41..397421093 100644 --- a/samples/jsonwebtoken/vclclient/MainClientFormU.dfm +++ b/samples/jsonwebtoken/vclclient/MainClientFormU.dfm @@ -82,7 +82,6 @@ object Form5: TForm5 Caption = 'Get a protected resource' TabOrder = 0 OnClick = btnGetClick - ExplicitTop = 2 end object btnLOGIN: TButton AlignWithMargins = True diff --git a/samples/jsonwebtoken_livevaliditywindow/JWTServer.dproj b/samples/jsonwebtoken_livevaliditywindow/JWTServer.dproj index 5b1640ecc..64951b49b 100644 --- a/samples/jsonwebtoken_livevaliditywindow/JWTServer.dproj +++ b/samples/jsonwebtoken_livevaliditywindow/JWTServer.dproj @@ -1,7 +1,7 @@  {7B54055A-5749-4136-9FE2-35FDBEEA874C} - 18.2 + 18.4 VCL JWTServer.dpr True diff --git a/samples/jsonwebtoken_livevaliditywindow/WebModuleUnit1.pas b/samples/jsonwebtoken_livevaliditywindow/WebModuleUnit1.pas index b436f68ec..765abd793 100644 --- a/samples/jsonwebtoken_livevaliditywindow/WebModuleUnit1.pas +++ b/samples/jsonwebtoken_livevaliditywindow/WebModuleUnit1.pas @@ -27,7 +27,6 @@ implementation {$R *.dfm} - uses AppControllerU, System.Generics.Collections, @@ -45,11 +44,12 @@ procedure TWebModule1.WebModuleCreate(Sender: TObject); 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 @@ procedure TWebModule1.WebModuleCreate(Sender: TObject); 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; diff --git a/samples/jsonwebtoken_livevaliditywindow/vclclient/JWTClient.dproj b/samples/jsonwebtoken_livevaliditywindow/vclclient/JWTClient.dproj index e82d3642b..c98e0cbe8 100644 --- a/samples/jsonwebtoken_livevaliditywindow/vclclient/JWTClient.dproj +++ b/samples/jsonwebtoken_livevaliditywindow/vclclient/JWTClient.dproj @@ -1,7 +1,7 @@  {E7317702-64D3-4A65-8734-030F3AE3DBBC} - 18.2 + 18.4 VCL JWTClient.dpr True diff --git a/samples/middleware/MiddlewareSamples.dproj b/samples/middleware/MiddlewareSamples.dproj index 0b8874c2d..1310da205 100644 --- a/samples/middleware/MiddlewareSamples.dproj +++ b/samples/middleware/MiddlewareSamples.dproj @@ -1,7 +1,7 @@  {0388D146-2B8B-427B-AEDD-EFD5F51D3139} - 18.2 + 18.4 VCL MiddlewareSamples.dpr True diff --git a/samples/outputcachewithredis/OutputCacheWithRedis.dpr b/samples/outputcachewithredis/OutputCacheWithRedis.dpr index 403fdc06f..b0f6af1a9 100644 --- a/samples/outputcachewithredis/OutputCacheWithRedis.dpr +++ b/samples/outputcachewithredis/OutputCacheWithRedis.dpr @@ -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; diff --git a/samples/outputcachewithredis/OutputCacheWithRedis.dproj b/samples/outputcachewithredis/OutputCacheWithRedis.dproj index 9725bac2a..81fb0ceca 100644 --- a/samples/outputcachewithredis/OutputCacheWithRedis.dproj +++ b/samples/outputcachewithredis/OutputCacheWithRedis.dproj @@ -1,7 +1,7 @@  {BE3A3D14-17E0-45C1-BD21-4710DE4CBCC2} - 18.2 + 18.4 VCL OutputCacheWithRedis.dpr True diff --git a/samples/renders/MyDataModuleU.pas b/samples/renders/MyDataModuleU.pas index 8415d7117..f7ffb455d 100644 --- a/samples/renders/MyDataModuleU.pas +++ b/samples/renders/MyDataModuleU.pas @@ -64,8 +64,6 @@ TMyDataModule = class(TDataModule) { Public declarations } end; -var - MyDataModule: TMyDataModule; implementation diff --git a/samples/renders/RenderSampleControllerU.pas b/samples/renders/RenderSampleControllerU.pas index cef501362..6fa6b88fe 100644 --- a/samples/renders/RenderSampleControllerU.pas +++ b/samples/renders/RenderSampleControllerU.pas @@ -229,7 +229,7 @@ procedure TRenderSampleController.GetCustomers_AsDataSet(CTX: TWebContext); lDM := TMyDataModule.Create(nil); try lDM.qryCustomers.Open; - Render(lDM.qryCustomers); + Render(lDM.qryCustomers, False); finally lDM.Free; end; diff --git a/samples/renders/renders.dproj b/samples/renders/renders.dproj index b0116ea5a..b269d5170 100644 --- a/samples/renders/renders.dproj +++ b/samples/renders/renders.dproj @@ -404,19 +404,19 @@ true - + .\ true - - + + .\ true - + .\ true @@ -428,13 +428,13 @@ true - - + + .\ true - + .\ true diff --git a/samples/routing/RoutingSampleControllerU.pas b/samples/routing/RoutingSampleControllerU.pas index 3c28b5516..3c76fb681 100644 --- a/samples/routing/RoutingSampleControllerU.pas +++ b/samples/routing/RoutingSampleControllerU.pas @@ -11,7 +11,7 @@ interface 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 @@ procedure TRoutingSampleController.CreatePerson; lPerson := Context.Request.BodyAs; 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 @@ procedure TRoutingSampleController.GetPerson(const id: Integer); Render(P); end; -procedure TRoutingSampleController.Index(CTX: TWebContext); +procedure TRoutingSampleController.Index; begin Render('This is the root path'); end; diff --git a/samples/sessions/SessionSample.dproj b/samples/sessions/SessionSample.dproj index 817a21bc5..cd3e5628c 100644 --- a/samples/sessions/SessionSample.dproj +++ b/samples/sessions/SessionSample.dproj @@ -1,7 +1,7 @@  {F9CBCE21-869A-478F-992C-88FCAC97BC8B} - 18.2 + 18.4 VCL SessionSample.dpr True diff --git a/samples/winecellarserver/www/css/styles.css b/samples/winecellarserver/www/css/styles.css index 6971a0c2b..4c56f8b3d 100644 --- a/samples/winecellarserver/www/css/styles.css +++ b/samples/winecellarserver/www/css/styles.css @@ -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; diff --git a/samples/winecellarserver/www/index.html b/samples/winecellarserver/www/index.html index 7675d93b0..2d908d266 100644 --- a/samples/winecellarserver/www/index.html +++ b/samples/winecellarserver/www/index.html @@ -1,63 +1,70 @@ + -Cellar - + Cellar + + -
- - - -
+
+ + + +
-
-
    -
    +
    +
      +
      -
      + -
      +
      - - + + - - + + - - + + - - + + - - + + - - -
      - - -
      -
      + + +
      + + +
      +
      -
      +
      - + - - -
      + + +
      -
      + - - + + - + + \ No newline at end of file diff --git a/sources/MVCFramework.JWT.pas b/sources/MVCFramework.JWT.pas index 09adfd413..62e0d029f 100644 --- a/sources/MVCFramework.JWT.pas +++ b/sources/MVCFramework.JWT.pas @@ -208,8 +208,9 @@ TJWT = class property LeewaySeconds: Cardinal read FLeewaySeconds; property RegClaimsToChecks: TJWTCheckableClaims read FRegClaimsToChecks write SetChecks; /// - /// 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. /// property LiveValidityWindowInSeconds: Cardinal read GetLiveValidityWindowInSeconds write SetLiveValidityWindowInSeconds; end; diff --git a/sources/MVCFramework.Middleware.JWT.pas b/sources/MVCFramework.Middleware.JWT.pas index f9786ccc1..322d18257 100644 --- a/sources/MVCFramework.Middleware.JWT.pas +++ b/sources/MVCFramework.Middleware.JWT.pas @@ -50,67 +50,40 @@ TMVCJWTAuthenticationMiddleware = class(TInterfacedObject, IMVCMiddleware) FLeewaySeconds: Cardinal; FLoginURLSegment: string; protected - 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 OnBeforeRouting( - AContext: TWebContext; - 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 - ); + 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 OnBeforeRouting(AContext: TWebContext; 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); 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 @@ constructor TMVCJWTAuthenticationMiddleware.Create(AAuthenticationHandler: IMVCA 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 @@ procedure TMVCJWTAuthenticationMiddleware.InternalRender( 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 @@ procedure TMVCJWTAuthenticationMiddleware.InternalRender( 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeControllerAction( 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeControllerAction( 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting( // 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting( 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting( 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 @@ procedure TMVCJWTAuthenticationMiddleware.OnBeforeRouting( 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;