Add models for Revel raw templates

This commit is contained in:
Sauyon Lee
2021-02-10 17:04:12 +00:00
parent 4932574083
commit 96d2777431
33 changed files with 1563 additions and 54 deletions

View File

@@ -104,8 +104,7 @@ module Revel {
* Note these don't actually generate the response, they return a struct which is then returned by the controller
* method, but it is very likely if a string is being rendered that it will end up sent to the user.
*
* The `Render` and `RenderTemplate` methods are excluded for now because both execute HTML templates, and deciding
* whether a particular value is exposed unescaped or not requires parsing the template.
* The `Render` and `RenderTemplate` methods are handled by `TemplateRender` below.
*
* The `RenderError` method can actually return HTML content, but again only via an HTML template if one exists;
* we assume it falls back to return plain text as this implies there is probably not an injection opportunity
@@ -216,4 +215,110 @@ module Revel {
inp = input and outp = output
}
}
/**
* A read in a Revel template that uses Revel's `raw` function.
*/
class RawTemplateRead extends HtmlTemplate::TemplateRead {
RawTemplateRead() { parent.getBody().regexpMatch("(?s)raw\\s.*") }
}
/**
* A write to a template argument field that is read raw inside of a template.
*/
private class RawTemplateArgument extends HTTP::ResponseBody::Range {
RawTemplateArgument() {
exists(TemplateRender render, RawTemplateRead read, VariableWithFields var |
render.getRenderedFile() = read.getFile() and
// if var is a.b.c, any rhs of a write to a, a.b, or a.b.cb
this = var.getParent*().getAWrite().getRhs()
|
var.getParent*() = render.getArgumentVariable() and
(
var = read.getReadVariable(render.getArgumentVariable())
or
// if no write or use of that variable exists, no VariableWithFields will be generated
// so we try to find a parent VariableWithFields
// this isn't covered by the 'getParent*' above because no match would be found at all
// for var
not exists(read.getReadVariable(render.getArgumentVariable())) and
exists(string fieldName | fieldName = read.getFieldName() |
var.getQualifiedName() =
render.getArgumentVariable().getQualifiedName() +
["." + fieldName.substring(0, fieldName.indexOf(".")), ""]
)
)
or
// a revel controller.Render(arg) will set controller.ViewArgs["arg"] = arg
exists(Variable arg | arg.getARead() = render.(ControllerRender).getAnArgument() |
var.getBaseVariable() = arg and
var.getQualifiedName() = read.getFieldName()
)
)
}
override string getAContentType() { result = "text/html" }
override HTTP::ResponseWriter getResponseWriter() { none() }
}
/**
* A render of a template.
*/
abstract class TemplateRender extends DataFlow::Node, TemplateInstantiation::Range {
/** Gets the name of the file that is rendered. */
abstract File getRenderedFile();
/** Gets the variable passed as an argument to the template. */
abstract VariableWithFields getArgumentVariable();
override DataFlow::Node getADataArgument() { result = this.getArgumentVariable().getAUse() }
}
/** A call to `Controller.Render`. */
private class ControllerRender extends TemplateRender, DataFlow::MethodCallNode {
ControllerRender() { this.getTarget().hasQualifiedName(packagePath(), "Controller", "Render") }
override DataFlow::Node getTemplateArgument() { none() }
override File getRenderedFile() {
exists(string controllerRe, string handlerRe, string pathRe |
controllerRe = "\\Q" + this.getReceiver().getType().getName() + "\\E" and
handlerRe = "\\Q" + this.getEnclosingCallable().getName() + "\\E" and
// find a file named '/views/<controller>/<handler>(.<template type>).html
pathRe = "/views/" + controllerRe + "/" + handlerRe + "(\\..*)?\\.html?"
|
result.getAbsolutePath().regexpMatch("(?i).*" + pathRe)
)
}
override VariableWithFields getArgumentVariable() {
exists(VariableWithFields base | base.getAUse().getASuccessor*() = this.getReceiver() |
result.getParent() = base and
result.getField().getName() = "ViewArgs"
)
}
}
/** A call to `Controller.RenderTemplate`. */
private class ControllerRenderTemplate extends TemplateRender, DataFlow::MethodCallNode {
ControllerRenderTemplate() {
this.getTarget().hasQualifiedName(packagePath(), "Controller", "RenderTemplate")
}
override DataFlow::Node getTemplateArgument() { result = this.getArgument(0) }
override File getRenderedFile() {
exists(string pathRe | pathRe = "\\Q" + this.getTemplateArgument().getStringValue() + "\\E" |
result.getAbsolutePath().regexpMatch(".*/" + pathRe)
)
}
override VariableWithFields getArgumentVariable() {
exists(VariableWithFields base | base.getAUse().getASuccessor*() = this.getReceiver() |
result.getParent() = base and
result.getField().getName() = "ViewArgs"
)
}
}
}

View File

@@ -34,7 +34,7 @@ func (c MyRoute) Handler2() revel.Result {
// BAD: the RenderBinary function copies an `io.Reader` to the user's browser.
buf := &bytes.Buffer{}
buf.WriteString(c.Params.Form.Get("someField"))
return c.RenderBinary(buf, "index.html", revel.Inline, time.Now())
return c.RenderBinary(buf, "index.html", revel.Inline, time.Now()) // $responsebody=buf
}
func (c MyRoute) Handler3() revel.Result {
@@ -42,14 +42,14 @@ func (c MyRoute) Handler3() revel.Result {
// means it will be given a safe content-type.
buf := &bytes.Buffer{}
buf.WriteString(c.Params.Form.Get("someField"))
return c.RenderBinary(buf, "index.txt", revel.Inline, time.Now())
return c.RenderBinary(buf, "index.txt", revel.Inline, time.Now()) // $responsebody=buf
}
func (c MyRoute) Handler4() revel.Result {
// GOOD: the RenderError function either uses an HTML template with probable escaping,
// or it uses content-type text/plain.
err := errors.New(c.Params.Form.Get("someField"))
return c.RenderError(err)
return c.RenderError(err) // $responsebody=err
}
func (c MyRoute) Handler5() revel.Result {
@@ -66,27 +66,27 @@ func (c MyRoute) Handler6() revel.Result {
func (c MyRoute) Handler7() revel.Result {
// BAD: straightforward XSS
return c.RenderHTML(c.Params.Form.Get("someField"))
return c.RenderHTML(c.Params.Form.Get("someField")) // $responsebody=call to Get
}
func (c MyRoute) Handler8() revel.Result {
// GOOD: uses JSON content-type
return c.RenderJSON(c.Params.Form.Get("someField"))
return c.RenderJSON(c.Params.Form.Get("someField")) // $responsebody=call to Get
}
func (c MyRoute) Handler9() revel.Result {
// GOOD: uses Javascript content-type
return c.RenderJSONP("callback", c.Params.Form.Get("someField"))
return c.RenderJSONP("callback", c.Params.Form.Get("someField")) // $responsebody=call to Get
}
func (c MyRoute) Handler10() revel.Result {
// GOOD: uses text content-type
return c.RenderText(c.Params.Form.Get("someField"))
return c.RenderText(c.Params.Form.Get("someField")) // $responsebody=call to Get
}
func (c MyRoute) Handler11() revel.Result {
// GOOD: uses xml content-type
return c.RenderXML(c.Params.Form.Get("someField"))
return c.RenderXML(c.Params.Form.Get("someField")) // $responsebody=call to Get
}
func (c MyRoute) Handler12() revel.Result {

View File

@@ -11,6 +11,18 @@ edges
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:29 | implicit dereference : Params |
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:29 | selection of Params : pointer type |
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:51 | call to Get |
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL |
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type |
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:53 | selection of Path |
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL |
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type |
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:53 | selection of Path |
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL |
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type |
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:58 | selection of Path |
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL |
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type |
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:58 | selection of Path |
nodes
| EndToEnd.go:36:18:36:25 | implicit dereference : Params | semmle.label | implicit dereference : Params |
| EndToEnd.go:36:18:36:25 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
@@ -18,6 +30,14 @@ nodes
| EndToEnd.go:69:22:69:29 | implicit dereference : Params | semmle.label | implicit dereference : Params |
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
| EndToEnd.go:69:22:69:51 | call to Get | semmle.label | call to Get |
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | semmle.label | implicit dereference : URL |
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
| examples/booking/app/init.go:36:44:36:53 | selection of Path | semmle.label | selection of Path |
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | semmle.label | implicit dereference : URL |
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
| examples/booking/app/init.go:40:49:40:58 | selection of Path | semmle.label | selection of Path |
#select
| EndToEnd.go:37:24:37:26 | buf | EndToEnd.go:36:18:36:25 | selection of Params : pointer type | EndToEnd.go:37:24:37:26 | buf | Cross-site scripting vulnerability due to $@. | EndToEnd.go:36:18:36:25 | selection of Params | user-provided value |
| EndToEnd.go:69:22:69:51 | call to Get | EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:51 | call to Get | Cross-site scripting vulnerability due to $@. | EndToEnd.go:69:22:69:29 | selection of Params | user-provided value |
| examples/booking/app/init.go:36:44:36:53 | selection of Path | examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:53 | selection of Path | Cross-site scripting vulnerability due to $@. | examples/booking/app/init.go:36:44:36:48 | selection of URL | user-provided value |
| examples/booking/app/init.go:40:49:40:58 | selection of Path | examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:58 | selection of Path | Cross-site scripting vulnerability due to $@. | examples/booking/app/init.go:40:49:40:53 | selection of URL | user-provided value |

View File

@@ -0,0 +1,17 @@
import go
import TestUtilities.InlineExpectationsTest
class HttpResponseBodyTest extends InlineExpectationsTest {
HttpResponseBodyTest() { this = "HttpResponseBodyTest" }
override string getARelevantTag() { result = "responsebody" }
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
exists(HTTP::ResponseBody rb |
rb.hasLocationInfo(file, line, _, _, _) and
element = rb.toString() and
value = rb.toString() and
tag = "responsebody"
)
}
}

View File

@@ -77,6 +77,12 @@ func (c myAppController) accessingParamsJSONIsUnsafe() {
useString(val2["name"].(string))
}
func (c myAppController) rawRead() { // $responsebody=argument corresponding to c
c.ViewArgs["Foo"] = "<p>raw HTML</p>" // $responsebody="<p>raw HTML</p>"
c.ViewArgs["Bar"] = "<p>not raw HTML</p>"
c.Render()
}
func accessingRequestDirectlyIsUnsafe(c *revel.Controller) {
useURLValues(c.Request.GetQuery()) // NOT OK
useURLValues(c.Request.Form) // NOT OK

View File

@@ -15,29 +15,29 @@
| Revel.go:69:11:69:18 | selection of Params : pointer type | Revel.go:69:11:69:34 | index expression | 69 |
| Revel.go:73:10:73:17 | selection of Params : pointer type | Revel.go:73:10:73:22 | selection of JSON | 73 |
| Revel.go:76:2:76:9 | selection of Params : pointer type | Revel.go:77:12:77:32 | type assertion | 76 |
| Revel.go:81:15:81:34 | call to GetQuery : Values | Revel.go:32:12:32:22 | index expression | 81 |
| Revel.go:81:15:81:34 | call to GetQuery : Values | Revel.go:33:12:33:23 | call to Get | 81 |
| Revel.go:82:15:82:28 | selection of Form : Values | Revel.go:32:12:32:22 | index expression | 82 |
| Revel.go:82:15:82:28 | selection of Form : Values | Revel.go:33:12:33:23 | call to Get | 82 |
| Revel.go:83:15:83:37 | selection of MultipartForm : pointer type | Revel.go:32:12:32:22 | index expression | 83 |
| Revel.go:83:15:83:37 | selection of MultipartForm : pointer type | Revel.go:33:12:33:23 | call to Get | 83 |
| Revel.go:84:12:84:32 | selection of ContentType | Revel.go:84:12:84:32 | selection of ContentType | 84 |
| Revel.go:85:12:85:36 | selection of AcceptLanguages : AcceptLanguages | Revel.go:85:12:85:48 | selection of Language | 85 |
| Revel.go:86:12:86:27 | selection of Locale | Revel.go:86:12:86:27 | selection of Locale | 86 |
| Revel.go:88:13:88:31 | call to GetForm : tuple type | Revel.go:32:12:32:22 | index expression | 88 |
| Revel.go:88:13:88:31 | call to GetForm : tuple type | Revel.go:33:12:33:23 | call to Get | 88 |
| Revel.go:91:13:91:40 | call to GetMultipartForm : tuple type | Revel.go:32:12:32:22 | index expression | 91 |
| Revel.go:91:13:91:40 | call to GetMultipartForm : tuple type | Revel.go:33:12:33:23 | call to Get | 91 |
| Revel.go:94:13:94:40 | call to GetMultipartForm : tuple type | Revel.go:95:11:95:35 | index expression | 94 |
| Revel.go:97:11:97:33 | selection of MultipartForm : pointer type | Revel.go:97:11:97:48 | index expression | 97 |
| Revel.go:99:28:99:46 | call to GetBody : Reader | Revel.go:100:10:100:13 | json | 99 |
| Revel.go:102:15:102:37 | call to Cookie : tuple type | Revel.go:103:12:103:28 | call to GetValue | 102 |
| Revel.go:105:12:105:48 | call to GetHttpHeader | Revel.go:105:12:105:48 | call to GetHttpHeader | 105 |
| Revel.go:107:12:107:36 | call to GetRequestURI | Revel.go:107:12:107:36 | call to GetRequestURI | 107 |
| Revel.go:109:15:109:41 | call to MultipartReader : tuple type | Revel.go:113:12:113:27 | type conversion | 109 |
| Revel.go:115:12:115:30 | call to Referer | Revel.go:115:12:115:30 | call to Referer | 115 |
| Revel.go:117:12:117:32 | call to UserAgent | Revel.go:117:12:117:32 | call to UserAgent | 117 |
| Revel.go:122:37:122:44 | &... : pointer type | Revel.go:123:12:123:18 | message | 122 |
| Revel.go:126:41:126:42 | &... : pointer type | Revel.go:127:12:127:12 | p | 126 |
| Revel.go:131:13:131:28 | selection of Header : pointer type | Revel.go:132:12:132:18 | tainted | 131 |
| Revel.go:134:14:134:29 | selection of Header : pointer type | Revel.go:135:12:135:22 | index expression | 134 |
| Revel.go:87:15:87:34 | call to GetQuery : Values | Revel.go:32:12:32:22 | index expression | 87 |
| Revel.go:87:15:87:34 | call to GetQuery : Values | Revel.go:33:12:33:23 | call to Get | 87 |
| Revel.go:88:15:88:28 | selection of Form : Values | Revel.go:32:12:32:22 | index expression | 88 |
| Revel.go:88:15:88:28 | selection of Form : Values | Revel.go:33:12:33:23 | call to Get | 88 |
| Revel.go:89:15:89:37 | selection of MultipartForm : pointer type | Revel.go:32:12:32:22 | index expression | 89 |
| Revel.go:89:15:89:37 | selection of MultipartForm : pointer type | Revel.go:33:12:33:23 | call to Get | 89 |
| Revel.go:90:12:90:32 | selection of ContentType | Revel.go:90:12:90:32 | selection of ContentType | 90 |
| Revel.go:91:12:91:36 | selection of AcceptLanguages : AcceptLanguages | Revel.go:91:12:91:48 | selection of Language | 91 |
| Revel.go:92:12:92:27 | selection of Locale | Revel.go:92:12:92:27 | selection of Locale | 92 |
| Revel.go:94:13:94:31 | call to GetForm : tuple type | Revel.go:32:12:32:22 | index expression | 94 |
| Revel.go:94:13:94:31 | call to GetForm : tuple type | Revel.go:33:12:33:23 | call to Get | 94 |
| Revel.go:97:13:97:40 | call to GetMultipartForm : tuple type | Revel.go:32:12:32:22 | index expression | 97 |
| Revel.go:97:13:97:40 | call to GetMultipartForm : tuple type | Revel.go:33:12:33:23 | call to Get | 97 |
| Revel.go:100:13:100:40 | call to GetMultipartForm : tuple type | Revel.go:101:11:101:35 | index expression | 100 |
| Revel.go:103:11:103:33 | selection of MultipartForm : pointer type | Revel.go:103:11:103:48 | index expression | 103 |
| Revel.go:105:28:105:46 | call to GetBody : Reader | Revel.go:106:10:106:13 | json | 105 |
| Revel.go:108:15:108:37 | call to Cookie : tuple type | Revel.go:109:12:109:28 | call to GetValue | 108 |
| Revel.go:111:12:111:48 | call to GetHttpHeader | Revel.go:111:12:111:48 | call to GetHttpHeader | 111 |
| Revel.go:113:12:113:36 | call to GetRequestURI | Revel.go:113:12:113:36 | call to GetRequestURI | 113 |
| Revel.go:115:15:115:41 | call to MultipartReader : tuple type | Revel.go:119:12:119:27 | type conversion | 115 |
| Revel.go:121:12:121:30 | call to Referer | Revel.go:121:12:121:30 | call to Referer | 121 |
| Revel.go:123:12:123:32 | call to UserAgent | Revel.go:123:12:123:32 | call to UserAgent | 123 |
| Revel.go:128:37:128:44 | &... : pointer type | Revel.go:129:12:129:18 | message | 128 |
| Revel.go:132:41:132:42 | &... : pointer type | Revel.go:133:12:133:12 | p | 132 |
| Revel.go:137:13:137:28 | selection of Header : pointer type | Revel.go:138:12:138:18 | tainted | 137 |
| Revel.go:140:14:140:29 | selection of Header : pointer type | Revel.go:141:12:141:22 | index expression | 140 |

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (C) 2012-2018 The Revel Framework Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,3 @@
Revel example adapted from [revel-examples](https://github.com/revel/revel-examples).
See `LICENSE` for license information.

View File

@@ -0,0 +1,98 @@
package controllers
import (
"github.com/revel/revel"
"codeql-go-tests/frameworks/Revel/examples/booking/app/models"
"github.com/revel/modules/orm/gorp/app/controllers"
)
type Application struct {
gorpController.Controller
}
func (c Application) AddUser() revel.Result {
if user := c.connected(); user != nil {
c.ViewArgs["user"] = user
}
return nil
}
func (c Application) connected() *models.User {
if c.ViewArgs["user"] != nil {
return c.ViewArgs["user"].(*models.User)
}
if username, ok := c.Session["user"]; ok {
return c.getUser(username.(string))
}
return nil
}
func (c Application) getUser(username string) (user *models.User) {
user = &models.User{}
c.Session.GetInto("fulluser", user, false)
if user.Username == username {
return user
}
c.Session["fulluser"] = user
return
}
func (c Application) Index() revel.Result {
if c.connected() != nil {
return c.Redirect(nil)
}
c.Flash.Error("Please log in first")
return c.Render()
}
func (c Application) Register() revel.Result {
return c.Render()
}
func (c Application) SaveUser(user models.User, verifyPassword string) revel.Result {
c.Validation.Required(verifyPassword)
c.Validation.Required(verifyPassword == user.Password).
MessageKey("Password does not match")
user.Validate(c.Validation)
if c.Validation.HasErrors() {
c.Validation.Keep()
c.FlashParams()
return c.Redirect(nil)
}
c.Session["user"] = user.Username
c.Flash.Success("Welcome, " + user.Name)
return c.Redirect(nil)
}
func (c Application) Login(username, password string, remember bool) revel.Result {
user := c.getUser(username)
if user != nil {
var err error
if err == nil {
c.Session["user"] = username
if remember {
c.Session.SetDefaultExpiration()
} else {
c.Session.SetNoExpiration()
}
c.Flash.Success("Welcome, " + username)
return c.Redirect(nil)
}
}
c.Flash.Out["username"] = username
c.Flash.Error("Login failed")
return c.Redirect(nil)
}
func (c Application) Logout() revel.Result {
for k := range c.Session {
delete(c.Session, k)
}
return c.Redirect(nil)
}

View File

@@ -0,0 +1,196 @@
//go:generate swagger generate spec -o swagger.json
// Package classification Swagger Hotel Example.
// Swagger Hotel Example
//
//
//
// Schemes: https
// Host: hotel.example.revelframework.com
// BasePath: /
// Version: 1.0.0
// License: MIT http://opensource.org/licenses/MIT
// Contact: Name<email@somehwere.com> https://www.somewhere.com
//
// Consumes:
// - application/json
// - application/x-www-form-urlencoded
//
// Produces:
// - text/html
//
//
//
//
// swagger:meta
package controllers
import (
"fmt"
"strings"
"codeql-go-tests/frameworks/Revel/examples/booking/app/models"
"github.com/revel/revel"
)
type Hotels struct {
Application
}
func (c Hotels) checkUser() revel.Result {
if user := c.connected(); user == nil {
c.Flash.Error("Please log in first")
return c.Redirect(nil)
}
return nil
}
func (c Hotels) Index() revel.Result {
c.Log.Info("Fetching index")
var bookings []*models.Booking
return c.Render(bookings)
}
// swagger:route GET /hotels/ListJson enter demo
//
// Enter Demo
//
//
// Consumes:
// - application/x-www-form-urlencoded
//
// Produces:
// - text/html
//
// Schemes: https
//
//
// Responses:
// 200: Success
// 401: Invalid User
// swagger:operation GET /demo demo
//
// Enter Demo
//
//
// ---
// produces:
// - text/html
// parameters:
// - name: user
// in: formData
// description: user
// required: true
// type: string
// - name: demo
// in: formData
// description: demo
// required: true
// type: string
// responses:
// '200':
// description: Success
// '401':
// description: Invalid User
func (c Hotels) ListJson(search string, size, page uint64) revel.Result {
if page == 0 {
page = 1
}
nextPage := page + 1
search = strings.TrimSpace(search)
var hotels []*models.Hotel
return c.RenderJSON(map[string]interface{}{"hotels": hotels, "search": search, "size": size, "page": page, "nextPage": nextPage}) // $responsebody=map literal
}
func (c Hotels) List(search string, size, page uint64) revel.Result {
if page == 0 {
page = 1
}
nextPage := page + 1
search = strings.TrimSpace(search)
var hotels []*models.Hotel
return c.Render(hotels, search, size, page, nextPage)
}
func (c Hotels) loadHotelById(id int) *models.Hotel {
var h interface{}
if h == nil {
return nil
}
return h.(*models.Hotel)
}
func (c Hotels) Show(id int) revel.Result {
hotel := c.loadHotelById(id)
if hotel == nil {
return c.NotFound("Hotel %d does not exist", id)
}
title := hotel.Name
return c.Render(title, hotel)
}
func (c Hotels) Settings() revel.Result {
return c.Render()
}
func (c Hotels) SaveSettings(password, verifyPassword string) revel.Result {
models.ValidatePassword(c.Validation, password)
c.Validation.Required(verifyPassword).
Message("Please verify your password")
c.Validation.Required(verifyPassword == password).
Message("Your password doesn't match")
if c.Validation.HasErrors() {
c.Validation.Keep()
return c.Redirect(nil)
}
c.Flash.Success("Password updated")
return c.Redirect(nil)
}
func (c Hotels) ConfirmBooking(id int, booking models.Booking) revel.Result {
hotel := c.loadHotelById(id) // $responsebody=call to loadHotelById
if hotel == nil {
return c.NotFound("Hotel %d does not exist", id)
}
title := fmt.Sprintf("Confirm %s booking", hotel.Name)
booking.Hotel = hotel
booking.User = c.connected()
booking.Validate(c.Validation)
if c.Validation.HasErrors() || c.Params.Get("revise") != "" {
c.Validation.Keep()
c.FlashParams()
return c.Redirect(nil)
}
if c.Params.Get("confirm") != "" {
c.Flash.Success("Thank you, %s, your confirmation number for %s is %d",
booking.User.Name, hotel.Name, booking.BookingId)
return c.Redirect(nil)
}
return c.Render(title, hotel, booking)
}
func (c Hotels) CancelBooking(id int) revel.Result {
c.Flash.Success(fmt.Sprintln("Booking cancelled for confirmation number", id))
return c.Redirect(nil)
}
func (c Hotels) Book(id int) revel.Result {
hotel := c.loadHotelById(id)
if hotel == nil {
return c.NotFound("Hotel %d does not exist", id)
}
title := "Book " + hotel.Name
return c.Render(title, hotel)
}

View File

@@ -0,0 +1,8 @@
package controllers
import "github.com/revel/revel"
func init() {
revel.InterceptMethod(Application.AddUser, revel.BEFORE)
revel.InterceptMethod(Hotels.checkUser, revel.BEFORE)
}

View File

@@ -0,0 +1,57 @@
package app
import (
"fmt"
"github.com/revel/revel"
"github.com/revel/revel/logger"
"net/http"
"os"
)
func init() {
// Filters is the default set of global filters.
revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead.
revel.RouterFilter, // Use the routing table to select the right Action
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
revel.SessionFilter, // Restore and write the session cookie.
revel.FlashFilter, // Restore and write the flash cookie.
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
revel.I18nFilter, // Resolve the requested language
HeaderFilter, // Add some security based headers
revel.InterceptorFilter, // Run interceptors around the action.
revel.CompressFilter, // Compress the result.
revel.ActionInvoker, // Invoke the action.
}
logger.LogFunctionMap["stdoutjson"] =
func(c *logger.CompositeMultiHandler, options *logger.LogOptions) {
// Set the json formatter to os.Stdout, replace any existing handlers for the level specified
c.SetJson(os.Stdout, options)
}
revel.AddInitEventHandler(func(event revel.Event, i interface{}) revel.EventResponse {
switch event {
case revel.ENGINE_BEFORE_INITIALIZED:
revel.AddHTTPMux("/this/is/a/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hi there, it worked", r.URL.Path) // $responsebody=selection of Path $responsebody="Hi there, it worked"
w.WriteHeader(200)
}))
revel.AddHTTPMux("/this/is/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hi there, shorter prefix", r.URL.Path) // $responsebody=selection of Path $responsebody="Hi there, shorter prefix"
w.WriteHeader(200)
}))
}
return 0
})
revel.OnAppStart(func() {}, 5)
}
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
// Add some common security headers
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
fc[0](c, fc[1:]) // Execute the next filter stage.
}

View File

@@ -0,0 +1,100 @@
package models
import (
"fmt"
"github.com/revel/revel"
"regexp"
"time"
)
type Booking struct {
BookingId int
UserId int
HotelId int
CheckInStr string
CheckOutStr string
CardNumber string
NameOnCard string
CardExpMonth int
CardExpYear int
Smoking bool
Beds int
// Transient
CheckInDate time.Time
CheckOutDate time.Time
User *User
Hotel *Hotel
}
// TODO: Make an interface for Validate() and then validation can pass in the
// key prefix ("booking.")
func (booking Booking) Validate(v *revel.Validation) {
v.Required(booking.User)
v.Required(booking.Hotel)
v.Required(booking.CheckInDate)
v.Required(booking.CheckOutDate)
v.Match(booking.CardNumber, regexp.MustCompile(`\d{16}`)).
Message("Credit card number must be numeric and 16 digits")
v.Check(booking.NameOnCard,
revel.Required{},
revel.MinSize{3},
revel.MaxSize{70},
)
}
func (b Booking) Total() int {
return b.Hotel.Price * b.Nights()
}
func (b Booking) Nights() int {
return int((b.CheckOutDate.Unix() - b.CheckInDate.Unix()) / 60 / 60 / 24)
}
const (
DATE_FORMAT = "Jan _2, 2006"
SQL_DATE_FORMAT = "2006-01-02"
)
func (b Booking) Description() string {
if b.Hotel == nil {
return ""
}
return fmt.Sprintf("%s, %s to %s",
b.Hotel.Name,
b.CheckInDate.Format(DATE_FORMAT),
b.CheckOutDate.Format(DATE_FORMAT))
}
func (b Booking) String() string {
return fmt.Sprintf("Booking(%s,%s)", b.User, b.Hotel.Name)
}
// These hooks work around two things:
// - Gorp's lack of support for loading relations automatically.
// - Sqlite's lack of support for datetimes.
func (b *Booking) PreInsert(_ interface{}) error {
b.UserId = b.User.UserId
b.HotelId = b.Hotel.HotelId
b.CheckInStr = b.CheckInDate.Format(SQL_DATE_FORMAT)
b.CheckOutStr = b.CheckOutDate.Format(SQL_DATE_FORMAT)
return nil
}
func (b *Booking) PostGet(exe interface{}) error {
var (
err error
)
if b.CheckInDate, err = time.Parse(SQL_DATE_FORMAT, b.CheckInStr); err != nil {
return fmt.Errorf("Error parsing check in date '%s' %s:", b.CheckInStr, err.Error())
}
if b.CheckOutDate, err = time.Parse(SQL_DATE_FORMAT, b.CheckOutStr); err != nil {
return fmt.Errorf("Error parsing check out date '%s' %s:", b.CheckOutStr, err.Error())
}
return nil
}

View File

@@ -0,0 +1,45 @@
package models
import (
"github.com/revel/revel"
)
type Hotel struct {
HotelId int
Name, Address string
City, State, Zip string
Country string
Price int
}
func (hotel *Hotel) Validate(v *revel.Validation) {
v.Check(hotel.Name,
revel.Required{},
revel.MaxSize{50},
)
v.MaxSize(hotel.Address, 100)
v.Check(hotel.City,
revel.Required{},
revel.MaxSize{40},
)
v.Check(hotel.State,
revel.Required{},
revel.MaxSize{6},
revel.MinSize{2},
)
v.Check(hotel.Zip,
revel.Required{},
revel.MaxSize{6},
revel.MinSize{5},
)
v.Check(hotel.Country,
revel.Required{},
revel.MaxSize{40},
revel.MinSize{2},
)
}

View File

@@ -0,0 +1,45 @@
package models
import (
"fmt"
"github.com/revel/revel"
"regexp"
)
type User struct {
UserId int
Name string
Username, Password string
HashedPassword []byte
}
func (u *User) String() string {
return fmt.Sprintf("User(%s)", u.Username)
}
var userRegex = regexp.MustCompile("^\\w*$")
func (user *User) Validate(v *revel.Validation) {
v.Check(user.Username,
revel.Required{},
revel.MaxSize{15},
revel.MinSize{4},
revel.Match{userRegex},
)
ValidatePassword(v, user.Password).
Key("user.Password")
v.Check(user.Name,
revel.Required{},
revel.MaxSize{100},
)
}
func ValidatePassword(v *revel.Validation, password string) *revel.ValidationResult {
return v.Check(password,
revel.Required{},
revel.MaxSize{15},
revel.MinSize{5},
)
}

View File

@@ -0,0 +1,114 @@
{{append . "moreStyles" "ui-lightness/jquery-ui-1.7.2.custom.css"}}
{{append . "moreScripts" "js/jquery-ui-1.7.2.custom.min.js"}}
{{template "header.html" .}}
<h1>Book hotel</h1>
<form method="POST" action="{{url "Hotels.Book" .hotel.HotelId}}">
<p>
<strong>Name:</strong> {{.hotel.Name}}
</p>
<p>
<strong>Address:</strong> {{.hotel.Address}}
</p>
<p>
<strong>City:</strong> {{.hotel.City}}
</p>
<p>
<strong>State:</strong> {{.hotel.State}}
</p>
<p>
<strong>Zip:</strong> {{.hotel.Zip}}
</p>
<p>
<strong>Country:</strong> {{.hotel.Country}}
</p>
<p>
<strong>Nightly rate:</strong> {{.hotel.Price}}
</p>
{{with $field := field "booking.CheckInDate" .}}
<p class="{{$field.ErrorClass}}">
<strong>Check In Date:</strong>
<input type="text" size="10" name="{{$field.Name}}" class="datepicker" value="{{$field.Flash}}">
* <span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "booking.CheckOutDate" .}}
<p class="{{$field.ErrorClass}}">
<strong>Check Out Date:</strong>
<input type="text" size="10" name="{{$field.Name}}" class="datepicker" value="{{$field.Flash}}">
* <span class="error">{{$field.Error}}</span>
</p>
{{end}}
<p>
<strong>Room preference:</strong>
{{with $field := field "booking.Beds" .}}
<select name="{{$field.Name}}">
{{option $field "1" "One king-size bed"}}
{{option $field "2" "Two double beds"}}
{{option $field "3" "Three beds"}}
</select>
{{end}}
</p>
<p>
<strong>Smoking preference:</strong>
{{with $field := field "booking.Smoking" .}}
{{radio $field "true"}} Smoking
{{radio $field "false"}} Non smoking
{{end}}
</p>
{{with $field := field "booking.CardNumber" .}}
<p class="{{$field.ErrorClass}}">
<strong>Credit Card #:</strong>
<input type="text" name="{{$field.Name}}" size="16" value="{{$field.Flash}}">
* <span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "booking.NameOnCard" .}}
<p class="{{$field.ErrorClass}}">
<strong>Credit Card Name:</strong>
<input type="text" name="{{$field.Name}}" size="16" value="{{$field.Flash}}">
* <span class="error">{{$field.Error}}</span>
</p>
{{end}}
<p>
<strong>Credit Card Expiry:</strong>
{{with $field := field "booking.CardExpMonth" .}}
<select name="{{$field.Name}}">
{{option $field "1" "Jan"}}
{{option $field "2" "Feb"}}
{{option $field "3" "Mar"}}
{{option $field "4" "Apr"}}
{{option $field "5" "May"}}
{{option $field "6" "Jun"}}
{{option $field "7" "Jul"}}
{{option $field "8" "Aug"}}
{{option $field "9" "Sep"}}
{{option $field "10" "Oct"}}
{{option $field "11" "Nov"}}
{{option $field "12" "Dec"}}
</select>
{{end}}
{{with $field := field "booking.CardExpYear" .}}
<select name="{{$field.Name}}">
{{option $field "2008" "2008"}}
{{option $field "2009" "2009"}}
{{option $field "2010" "2010"}}
{{option $field "2011" "2011"}}
{{option $field "2012" "2012"}}
</select>
{{end}}
</p>
<p class="buttons">
<input type="submit" value="Proceed">
<a href="{{url "Hotels.Show" .hotel.HotelId}}">Cancel</a>
</p>
</form>
<script type="text/javascript" charset="utf-8">
$(function() {
$(".datepicker").datepicker({dateFormat: 'yy-mm-dd'});
});
</script>
{{template "footer.html" .}}

View File

@@ -0,0 +1,58 @@
{{template "header.html" .}}
<h1>Confirm hotel booking</h1>
<form method="POST" action="{{url "Hotels.ConfirmBooking" .hotel.HotelId}}">
<p>
<strong>Name:</strong> {{raw .hotel.Name}}
</p>
<p>
<strong>Address:</strong> {{.hotel.Address}}
</p>
<p>
<strong>City:</strong> {{.hotel.City}}
</p>
<p>
<strong>State:</strong> {{.hotel.State}}
</p>
<p>
<strong>Zip:</strong> {{.hotel.Zip}}
</p>
<p>
<strong>Country:</strong> {{.hotel.Country}}
</p>
<p>
<strong>Nightly rate:</strong> {{.hotel.Price}}
</p>
<p>
<strong>Beds:</strong> {{.booking.Beds}}
<input type="hidden" name="booking.Beds" value="{{.booking.Beds}}">
</p>
<p>
<strong>Total:</strong> {{.booking.Total}} {{/* .formatCurrency('USD') */}}
</p>
<p>
<strong>Check in date:</strong> {{.booking.CheckInDate.Format "2006-01-02"}}
<input type="hidden" name="booking.CheckInDate" value="{{.booking.CheckInDate.Format "2006-01-02"}}">
</p>
<p>
<strong>Check out date:</strong> {{.booking.CheckOutDate.Format "2006-01-02"}}
<input type="hidden" name="booking.CheckOutDate" value="{{.booking.CheckOutDate.Format "2006-01-02"}}">
</p>
<p>
<strong>Credit card #:</strong> {{.booking.CardNumber}}
<input type="hidden" name="booking.CardNumber" value="{{.booking.CardNumber}}">
<input type="hidden" name="booking.NameOnCard" value="{{.booking.NameOnCard}}">
<input type="hidden" name="booking.CardExpMonth" value="{{.booking.CardExpMonth}}">
<input type="hidden" name="booking.CardExpYear" value="{{.booking.CardExpYear}}">
<input type="hidden" name="booking.Smoking" value="{{.booking.Smoking}}">
</p>
<p class="buttons">
<input type="submit" value="Confirm" name="confirm">
<input type="submit" value="Revise" name="revise">
<a href="{{url "Hotels.Show" .hotel.HotelId}}">Cancel</a>
</p>
</form>
{{template "footer.html" .}}

View File

@@ -0,0 +1,111 @@
{{set . "title" "Search"}}
{{template "header.html" .}}
<h1>Search Hotels</h1>
<p>
<input type="text" id="search" size="30">
<input type="submit" id="submit" value="Find Hotels">
<br>
Maximum results:
<select id="size">
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
</p>
<div id="result">
</div>
<script type="text/javascript" charset="utf-8">
// Rebuild state
$('#search').val(sessvars.search)
if(sessvars.page == undefined) {
sessvars.page = 1
}
if(sessvars.size == undefined) {
sessvars.size = 10
}
$('#size option[value='+sessvars.size+']').attr('selected', 'true')
// Search function
var search = function() {
sessvars.search = $('#search').val()
sessvars.size = $('#size').val()
$.get("/hotels/list", {
"search": sessvars.search,
"size": sessvars.size,
"page": sessvars.page
}, function(data) {
$('#result').html(data)
$('#content').css('visibility', 'visible')
})
}
// Events handler
$('#submit').click(function() {
sessvars.page = 1
search()
})
$('#search').keyup(function() {
sessvars.page = 1
search()
})
$('#nextPage').live('click', function(e) {
sessvars.page = $(this).attr('href')
e.preventDefault()
search()
})
// Init
if(sessvars.search != undefined) {
$('#content').css('visibility', 'hidden')
search()
}
</script>
<h1>Current Hotel Bookings</h1>
{{if not .bookings}}
<p>
No Bookings Found
</p>
{{else}}
<table>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>City, State</th>
<th>Check in</th>
<th>Check out</th>
<th>Confirmation number</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{{range .bookings}}
<tr>
<td>{{.Hotel.Name}}</td>
<td>{{.Hotel.Address}}</td>
<td>{{.Hotel.City}}, {{.Hotel.State}}, {{.Hotel.Country}}</td>
<td>{{.CheckInDate.Format "2006-01-02"}}</td>
<td>{{.CheckOutDate.Format "2006-01-02"}}</td>
<td>{{raw .BookingId}}</td> <!-- not caught because it is inside a range and no parsing is done -->
<td>
<form id="d{{.BookingId}}" method="POST" action="/bookings/{{.BookingId}}/cancel">
<a href="javascript:document.getElementById('d{{.BookingId}}').submit();">Cancel</a>
</form>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{template "footer.html" .}}

View File

@@ -0,0 +1,34 @@
{{if .hotels}}
<table>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>City, State</th>
<th width="8%">Zip</th>
<th width="13%">Action</th>
</tr>
</thead>
<tbody>
{{range .hotels}}
<tr>
<td>{{.Name}}</td>
<td>{{.Address}}</td>
<td>{{.City}}, {{.State}}, {{.Country}}</td>
<td>{{.Zip}}</td>
<td>
<a href="{{url "Hotels.Show" .HotelId}}">View Hotel</a>
</td>
</tr>
{{end}}
</tbody>
</table>
<p>
<a id="nextPage" href="{{.nextPage}}">More results</a>
</p>
{{else}}
<p>
No more results
</p>
{{end}}

View File

@@ -0,0 +1,27 @@
{{set . "title" "Settings"}}
{{template "header.html" .}}
<h1>Change your password</h1>
<form method="POST" action="{{url "Hotels.SaveSettings"}}">
{{with $field := field "password" .}}
<p class="{{$field.ErrorClass}}">
<strong>Password:</strong>
<input type="password" name="{{$field.Name}}" size="16"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "verifyPassword" .}}
<p class="{{$field.ErrorClass}}">
<strong>Verify password:</strong>
<input type="password" name="{{$field.Name}}" size="16"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
<p class="buttons">
<input type="submit" value="Save"> <a href="{{url "Hotels.Index"}}">Cancel</a>
</p>
</form>
{{template "footer.html" .}}

View File

@@ -0,0 +1,37 @@
{{template "header.html" .}}
<h1>View hotel</h1>
{{with .hotel}}
<form action="{{url "Hotels.Book" .HotelId}}">
<p>
<strong>Name:</strong> {{.Name}}
</p>
<p>
<strong>Address:</strong> {{.Address}}
</p>
<p>
<strong>City:</strong> {{.City}}
</p>
<p>
<strong>State:</strong> {{.State}}
</p>
<p>
<strong>Zip:</strong> {{.Zip}}
</p>
<p>
<strong>Country:</strong> {{.Country}}
</p>
<p>
<strong>Nightly rate:</strong> {{.Price}}
</p>
<p class="buttons">
<input type="submit" value="Book Hotel">
<a href="{{url "Hotels.Index"}}">Back to search</a>
</p>
</form>
{{end}}
{{template "footer.html" .}}

View File

@@ -0,0 +1,32 @@
{{set . "title" "Home"}}
{{template "header.html" .}}
<div id="login">
(try with demo/demo)
<form action="{{url "Application.Login"}}" id="formLogin" method="POST">
<p class="field">
<label>Login Name:</label>
<input type="text" name="username" size="19" value="{{.flash.username}}" />
</p>
<p class="field">
<label>Password:</label>
<input type="password" name="password" size="19"/>
</p>
<p class="field">
<label>Remember:</label>
<input type="checkbox" name="remember" value="true"/>
</p>
<p class="buttons">
<input type="submit" value="Account login" />
</p>
</form>
<p>
<a href="{{url "Application.Register"}}">Register New User</a>
</p>
</div>
{{template "footer.html" .}}

View File

@@ -0,0 +1,37 @@
{{set . "title" "Register"}}
{{template "header.html" .}}
<h1>Register:</h1>
<form action="{{url "Application.SaveUser"}}" method="POST">
{{with $field := field "user.Username" .}}
<p class="{{$field.ErrorClass}}">
<strong>Username:</strong>
<input type="text" name="{{$field.Name}}" size="16" value="{{$field.Flash}}"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "user.Name" .}}
<p class="{{$field.ErrorClass}}">
<strong>Real name:</strong> <input type="text" name="{{$field.Name}}" size="16" value="{{$field.Flash}}"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "user.Password" .}}
<p class="{{$field.ErrorClass}}">
<strong>Password:</strong> <input type="password" name="{{$field.Name}}" size="16" value="{{$field.Flash}}"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
{{with $field := field "verifyPassword" .}}
<p class="{{$field.ErrorClass}}">
<strong>Verify password:</strong> <input type="password" name="{{$field.Name}}" size="16" value="{{$field.Flash}}"> *
<span class="error">{{$field.Error}}</span>
</p>
{{end}}
<p class="buttons">
<input type="submit" value="Register"> <a href="{{url "Application.Index"}}">Cancel</a>
</p>
</form>
{{template "footer.html" .}}

View File

@@ -0,0 +1,8 @@
</div>
<div id="footer">
Created with the <a href="http://github.com/revel/revel">Revel framework</a> and really inspirated from the booking sample application provided by <a href="http://www.playframework.org">play framework</a>, which was really inspired by the booking sample application provided by the <a href="http://seamframework.org/">seam framework</a>.
</div>
</body>
</html>

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>{{.title}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css">
{{range .moreStyles}}
<link rel="stylesheet" type="text/css" href="/public/{{.}}">
{{end}}
<script src="/public/js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
<script src="/public/js/sessvars.js" type="text/javascript" charset="utf-8"></script>
{{range .moreScripts}}
<script src="/public/{{.}}" type="text/javascript" charset="utf-8"></script>
{{end}}
</head>
<body>
<div id="header">
<h1>revel framework booking demo</h1>
{{if .user}}
<div id="options">
Connected as {{ (session "fulluser" .).Username }}
|
<a href="{{url "Hotels.Index"}}">Search</a>
|
<a href="{{url "Hotels.Settings"}}">Settings</a>
|
<a href="{{url "Application.Logout"}}">Logout</a>
</div>
{{end}}
</div>
<div id="content">
{{if .flash.error}}
<p class="fError">
<strong>{{.flash.error}}</strong>
</p>
{{end}}
{{if .flash.success}}
<p class="fSuccess">
<strong>{{.flash.success}}</strong>
</p>
{{end}}

View File

@@ -3,11 +3,7 @@ module codeql-go-tests/frameworks/Revel
go 1.14
require (
github.com/github/depstubber v0.0.0-20200916130315-f3217697abd4 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/mattn/go-colorable v0.1.7 // indirect
github.com/revel/config v1.0.0 // indirect
github.com/revel/modules v1.0.0
github.com/revel/revel v1.0.0
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
)

View File

@@ -0,0 +1,18 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/revel/modules/orm/gorp/app/controllers, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/revel/modules/orm/gorp/app/controllers (exports: Controller; functions: )
// Package controllers is a stub of github.com/revel/modules/orm/gorp/app/controllers, generated by depstubber.
package gorpController
import (
revel "github.com/revel/revel"
)
type Controller struct {
*revel.Controller
Txn interface{}
Db interface{}
}

View File

@@ -0,0 +1,114 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/revel/revel/logger, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/revel/revel/logger (exports: MultiLogger,LogOptions,CompositeMultiHandler; functions: LogFunctionMap)
// Package logger is a stub of github.com/revel/revel/logger, generated by depstubber.
package logger
import (
fmt "fmt"
io "io"
time "time"
)
type CallStack interface {
Format(_ fmt.State, _ int32)
}
type CompositeMultiHandler struct {
DebugHandler LogHandler
InfoHandler LogHandler
WarnHandler LogHandler
ErrorHandler LogHandler
CriticalHandler LogHandler
}
func (_ *CompositeMultiHandler) Disable(_ ...LogLevel) {}
func (_ *CompositeMultiHandler) Log(_ *Record) error {
return nil
}
func (_ *CompositeMultiHandler) SetHandler(_ LogHandler, _ bool, _ LogLevel) {}
func (_ *CompositeMultiHandler) SetHandlers(_ LogHandler, _ *LogOptions) {}
func (_ *CompositeMultiHandler) SetJson(_ io.Writer, _ *LogOptions) {}
func (_ *CompositeMultiHandler) SetJsonFile(_ string, _ *LogOptions) {}
func (_ *CompositeMultiHandler) SetTerminal(_ io.Writer, _ *LogOptions) {}
func (_ *CompositeMultiHandler) SetTerminalFile(_ string, _ *LogOptions) {}
type ContextMap map[string]interface{}
func (_ ContextMap) Add(_ string, _ interface{}) {}
func (_ ContextMap) StringMap() map[string]string {
return nil
}
var LogFunctionMap map[string]func(*CompositeMultiHandler, *LogOptions) = nil
type LogHandler interface {
Log(_ *Record) error
}
type LogLevel int
type LogOptions struct {
Ctx interface{}
ReplaceExistingHandler bool
HandlerWrap ParentLogHandler
Levels []LogLevel
ExtendedOptions map[string]interface{}
}
func (_ *LogOptions) GetBoolDefault(_ string, _ bool) bool {
return false
}
func (_ *LogOptions) GetIntDefault(_ string, _ int) int {
return 0
}
func (_ *LogOptions) GetStringDefault(_ string, _ string) string {
return ""
}
func (_ *LogOptions) SetExtendedOptions(_ ...interface{}) {}
type MultiLogger interface {
Crit(_ string, _ ...interface{})
Critf(_ string, _ ...interface{})
Debug(_ string, _ ...interface{})
Debugf(_ string, _ ...interface{})
Error(_ string, _ ...interface{})
Errorf(_ string, _ ...interface{})
Fatal(_ string, _ ...interface{})
Fatalf(_ string, _ ...interface{})
Info(_ string, _ ...interface{})
Infof(_ string, _ ...interface{})
New(_ ...interface{}) MultiLogger
Panic(_ string, _ ...interface{})
Panicf(_ string, _ ...interface{})
SetHandler(_ LogHandler)
SetStackDepth(_ int) MultiLogger
Warn(_ string, _ ...interface{})
Warnf(_ string, _ ...interface{})
}
type ParentLogHandler interface {
SetChild(_ LogHandler) LogHandler
}
type Record struct {
Message string
Time time.Time
Level LogLevel
Call CallStack
Context ContextMap
}

View File

@@ -0,0 +1,62 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/revel/revel/session, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/revel/revel/session (exports: Session; functions: )
// Package session is a stub of github.com/revel/revel/session, generated by depstubber.
package session
import (
time "time"
)
type Session map[string]interface{}
func (_ Session) Del(_ string) {}
func (_ Session) Empty() bool {
return false
}
func (_ Session) Get(_ string) (interface{}, error) {
return nil, nil
}
func (_ Session) GetDefault(_ string, _ interface{}, _ interface{}) interface{} {
return nil
}
func (_ Session) GetExpiration(_ time.Duration) time.Time {
return time.Time{}
}
func (_ Session) GetInto(_ string, _ interface{}, _ bool) (interface{}, error) {
return nil, nil
}
func (_ Session) GetProperty(_ string, _ interface{}) (interface{}, error) {
return nil, nil
}
func (_ Session) ID() string {
return ""
}
func (_ Session) Load(_ map[string]string) {}
func (_ Session) Serialize() map[string]string {
return nil
}
func (_ Session) SessionTimeoutExpiredOrMissing() bool {
return false
}
func (_ Session) Set(_ string, _ interface{}) error {
return nil
}
func (_ Session) SetDefaultExpiration() {}
func (_ Session) SetNoExpiration() {}

View File

@@ -9,6 +9,8 @@ package revel
import (
context "context"
logger "github.com/revel/revel/logger"
session "github.com/revel/revel/session"
io "io"
multipart "mime/multipart"
http "net/http"
@@ -53,6 +55,12 @@ func (_ *ActionDefinition) String() string {
return ""
}
func ActionInvoker(_ *Controller, _ []Filter) {}
func AddHTTPMux(_ string, _ interface{}) {}
func AddInitEventHandler(_ EventHandler) {}
type ContentDisposition string
var (
@@ -73,12 +81,12 @@ type Controller struct {
Response *Response
Result Result
Flash Flash
Session interface{}
Session session.Session
Params *Params
Args map[string]interface{}
ViewArgs map[string]interface{}
Validation *Validation
Log interface{}
Log logger.MultiLogger
}
func (_ *Controller) Destroy() {}
@@ -230,6 +238,43 @@ func (_ *Error) Error() string {
func (_ *Error) SetLink(_ string) {}
type Event int
const (
TEMPLATE_REFRESH_REQUESTED Event = iota
TEMPLATE_REFRESH_COMPLETED
REVEL_BEFORE_MODULES_LOADED
REVEL_AFTER_MODULES_LOADED
ENGINE_BEFORE_INITIALIZED
ENGINE_STARTED
ENGINE_SHUTDOWN_REQUEST
ENGINE_SHUTDOWN
ROUTE_REFRESH_REQUESTED
ROUTE_REFRESH_COMPLETED
REVEL_FAILURE
)
type EventHandler func(typeOf Event, value interface{}) (responseOf EventResponse)
type EventResponse int
type Filter func(c *Controller, filterChain []Filter)
var Filters = []Filter{}
var (
PanicFilter Filter = nil
RouterFilter Filter = nil
FilterConfiguringFilter Filter = nil
ParamsFilter Filter = nil
SessionFilter Filter = nil
FlashFilter Filter = nil
ValidationFilter Filter = nil
I18nFilter Filter = nil
InterceptorFilter Filter = nil
CompressFilter Filter = nil
)
type Flash struct {
Data map[string]string
Out map[string]string
@@ -241,6 +286,34 @@ func (_ Flash) Success(_ string, _ ...interface{}) {}
var HTTP_QUERY int = 0
func InterceptMethod(intc InterceptorMethod, when When) {}
type InterceptorMethod interface{}
type Match struct {
Regexp *regexp.Regexp
}
func (_ Match) DefaultMessage() string {
return ""
}
func (_ Match) IsSatisfied(obj interface{}) bool {
return false
}
type MaxSize struct {
Max int
}
func (_ MaxSize) DefaultMessage() string {
return ""
}
func (_ MaxSize) IsSatisfied(obj interface{}) bool {
return false
}
type MethodArg struct {
Name string
Type reflect.Type
@@ -253,6 +326,18 @@ type MethodType struct {
Index int
}
type MinSize struct {
Min int
}
func (_ MinSize) DefaultMessage() string {
return ""
}
func (_ MinSize) IsSatisfied(obj interface{}) bool {
return false
}
type Module struct {
Name string
ImportPath string
@@ -276,6 +361,8 @@ type MultipartForm struct {
Value url.Values
}
func OnAppStart(_ func(), _ ...int) {}
type OutResponse struct {
Server ServerResponse
}
@@ -398,6 +485,16 @@ func (_ *Request) UserAgent() string {
return ""
}
type Required struct{}
func (_ Required) DefaultMessage() string {
return ""
}
func (_ Required) IsSatisfied(obj interface{}) bool {
return false
}
type Response struct {
Status int
ContentType string
@@ -695,3 +792,12 @@ type Validator interface {
DefaultMessage() string
IsSatisfied(_ interface{}) bool
}
type When int
const (
BEFORE When = iota
AFTER
PANIC
FINALLY
)

View File

@@ -1,21 +1,9 @@
# github.com/github/depstubber v0.0.0-20200916130315-f3217697abd4
## explicit
github.com/github/depstubber
# github.com/go-stack/stack v1.8.0
## explicit
github.com/go-stack/stack
# github.com/mattn/go-colorable v0.1.7
## explicit
github.com/mattn/go-colorable
# github.com/revel/config v1.0.0
## explicit
github.com/revel/config
# github.com/revel/modules v1.0.0
## explicit
github.com/revel/modules
# github.com/revel/revel v1.0.0
## explicit
github.com/revel/revel
# golang.org/x/net v0.0.0-20200904194848-62affa334b73
## explicit
golang.org/x/net

View File

@@ -0,0 +1,2 @@
{{raw .Foo}}
{{.Bar}}