This commit is contained in:
Niko Abeler 2023-07-19 20:59:43 +02:00
parent 10e0bde07b
commit 4540797cce
71 changed files with 0 additions and 8515 deletions

26
.gitignore vendored
View File

@ -1,26 +0,0 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
users/
.vscode/
*.swp

View File

@ -1,3 +0,0 @@
{
"editor.formatOnSave": true,
}

View File

@ -1,33 +0,0 @@
##
## Build Container
##
FROM golang:1.19-alpine as build
RUN apk add --no-cache git
WORKDIR /tmp/owl
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN go build -o ./out/owl ./cmd/owl
##
## Run Container
##
FROM alpine:3.9
RUN apk add ca-certificates
COPY --from=build /tmp/owl/out/ /bin/
# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the binary program produced by `go install`
ENTRYPOINT ["/bin/owl"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,200 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="owl.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="mirror_symmetry"
start_point="101.5,113.98198"
end_point="101.5,177.55836"
center_point="101.5,145.77017"
id="path-effect4762"
is_visible="true"
mode="free"
discard_orig_path="false"
fuse_paths="true"
oposite_fuse="false" />
<inkscape:path-effect
effect="mirror_symmetry"
start_point="101.6,77.962793"
end_point="101.6,178.13471"
center_point="101.6,128.04875"
id="path-effect4630"
is_visible="true"
mode="free"
discard_orig_path="false"
fuse_paths="true"
oposite_fuse="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="-5.4384962"
inkscape:cy="476.07169"
inkscape:document-units="mm"
inkscape:current-layer="layer5"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1391"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Body"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(0,-197)" />
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Front"
transform="translate(0,-197)" />
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Ref"
style="display:inline"
transform="translate(0,-197)" />
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Feet"
transform="translate(0,-197)" />
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Nose"
transform="translate(0,-197)" />
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Eyes"
transform="translate(0,-197)">
<path
style="display:inline;opacity:1;fill:#686560;fill-opacity:1;stroke:#000000;stroke-width:1.46500003;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 72.665922,98.468004 c -3.804657,-4.064063 -8.937651,-7.726186 -7.559523,-20.505208 1.511903,2.173362 6.898064,8.031993 12.095236,8.031993 7.291682,0 22.616673,0.08369 24.398365,0.09355 1.78169,-0.0099 17.10668,-0.09355 24.39836,-0.09355 5.19718,0 10.58334,-5.858631 12.09524,-8.031993 1.37813,12.779022 -3.75487,16.441145 -7.55952,20.505208 0,0 4.84169,14.668796 9.88119,20.229646 10.76577,11.87955 9.28012,38.96737 -13.7775,54.51257 -4.9083,3.30912 -14.73063,5.13452 -25.03777,4.90519 -10.307138,0.22933 -20.129467,-1.59607 -25.037771,-4.90519 -23.057616,-15.5452 -24.543272,-42.63302 -13.777496,-54.51257 5.039494,-5.56085 9.881189,-20.229646 9.881189,-20.229646 z"
id="path46"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsccssc"
inkscape:path-effect="#path-effect4630"
inkscape:original-d="m 72.665922,98.468004 c -3.804657,-4.064063 -8.937651,-7.726186 -7.559523,-20.505208 1.511903,2.173362 6.898064,8.031993 12.095236,8.031993 7.748513,0 24.568455,0.0945 24.568455,0.0945 9.17095,40.967981 -1.73329,86.382581 24.08866,86.819411 -14.73188,7.45015 -40.288984,6.3743 -49.296521,0.30152 -23.057616,-15.5452 -24.543272,-42.63302 -13.777496,-54.51257 5.039494,-5.56085 9.881189,-20.229646 9.881189,-20.229646 z"
transform="matrix(0.89013051,0,0,0.89013051,-40.43726,131.70004)" />
<path
style="display:inline;fill:#fbe9c4;fill-opacity:1;stroke:#262626;stroke-width:4.06500006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 74.568197,129.95654 c -1.207452,8.188 -1.187282,34.70878 17.572973,44.03265 3.623482,1.36045 5.80487,3.13501 9.35883,3.62684 3.55396,-0.49183 5.73535,-2.26639 9.35883,-3.62684 18.76025,-9.32387 18.78042,-35.84465 17.57297,-44.03265 C 126.73222,117.4929 107.35585,115.8851 101.5,115.68436 95.644152,115.8851 76.267784,117.4929 74.568197,129.95654 Z"
id="path4760"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccscc"
inkscape:path-effect="#path-effect4762"
inkscape:original-d="m 74.568197,129.95654 c -1.207452,8.188 -1.187282,34.70878 17.572973,44.03265 4.717147,1.77107 6.990299,4.24396 13.02939,3.67496 5.2517,-4.28707 -4.51571,-38.74118 3.27405,-50.98166 11.76071,-18.48023 -5.27857,-11.02487 -5.27857,-11.02487 0,0 -26.593322,-0.4009 -28.597843,14.29892 z"
transform="matrix(0.89013051,0,0,0.89013051,-40.43726,131.70004)" />
<rect
style="opacity:1;fill:#674808;fill-opacity:1;stroke:#efc48c;stroke-width:2.72825003;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4767"
width="23.046696"
height="2.0186887"
x="39.805622"
y="258.96619" />
<rect
style="opacity:1;fill:#674808;fill-opacity:1;stroke:#efc48c;stroke-width:2.72825003;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4767-7"
width="23.046698"
height="2.0186887"
x="39.805622"
y="266.51617" />
<rect
style="opacity:1;fill:#674808;fill-opacity:1;stroke:#efc48c;stroke-width:2.72825003;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4767-7-0"
width="23.046698"
height="2.0186887"
x="39.80563"
y="274.1886" />
<path
style="display:inline;opacity:1;fill:#f1cd6b;fill-opacity:1;stroke:#000000;stroke-width:1.21502817;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 37.113903,280.91958 a 11.943909,11.943909 0 0 0 -11.943581,11.94359 h 23.88762 A 11.943909,11.943909 0 0 0 37.113903,280.91958 Z"
id="path4732"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="display:inline;opacity:1;fill:#f1cd6b;fill-opacity:1;stroke:#000000;stroke-width:1.21502817;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 62.767977,281.00369 A 11.943909,11.943909 0 0 0 50.824392,292.94728 H 74.712015 A 11.943909,11.943909 0 0 0 62.767977,281.00369 Z"
id="path4732-2" />
<path
style="fill:#dd9829;fill-opacity:1;stroke:#000000;stroke-width:1.21502817;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 45.440938,246.0794 c 0,0 2.139571,9.72892 5.947619,9.7541 3.839356,0.0254 6.126056,-9.7541 6.126056,-9.7541 l -6.140761,-8.69398 z"
id="path4721"
inkscape:connector-curvature="0"
sodipodi:nodetypes="caccc" />
<path
style="opacity:1;fill:#fedf89;fill-opacity:1;stroke:#000000;stroke-width:1.95828712;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 34.506691,216.30236 a 18.692076,18.692076 0 0 0 -18.692058,18.69205 18.692076,18.692076 0 0 0 18.692058,18.69206 18.692076,18.692076 0 0 0 16.928008,-10.81661 18.692078,18.692078 0 0 0 16.968945,10.90079 18.692078,18.692078 0 0 0 18.69206,-18.69206 18.692078,18.692078 0 0 0 -18.69206,-18.69252 18.692078,18.692078 0 0 0 -16.929847,10.81983 18.692076,18.692076 0 0 0 -16.967106,-10.90354 z"
id="path4607"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#fffefc;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.955116,221.31207 a 13.794374,13.429993 0 0 0 -13.301924,9.88009 13.794374,13.429993 0 0 0 -13.277543,-9.79636 13.794374,13.429993 0 0 0 -13.794111,13.43025 13.794374,13.429993 0 0 0 13.794111,13.4298 13.794374,13.429993 0 0 0 13.301003,-9.92837 13.794374,13.429993 0 0 0 13.278464,9.8442 13.794374,13.429993 0 0 0 13.794572,-13.4298 13.794374,13.429993 0 0 0 -13.794572,-13.42981 z"
id="path4609"
inkscape:connector-curvature="0" />
<ellipse
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4611"
cx="40.394402"
cy="235.28864"
rx="8.9999876"
ry="8.8738194" />
<circle
style="opacity:1;fill:#fffefc;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4609-3"
cy="230.89021"
cx="35.68087"
r="5.6899729" />
<ellipse
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4611-9"
cx="-63.020538"
cy="235.37274"
rx="8.9999876"
ry="8.8738194"
transform="scale(-1,1)" />
<circle
style="opacity:1;fill:#fffefc;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4609-3-1"
cx="-67.734055"
cy="230.97433"
transform="scale(-1,1)"
r="5.0387244" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,48 +0,0 @@
package owl_test
import (
"h4kor/owl-blogs"
"h4kor/owl-blogs/test/assertions"
"net/http"
"testing"
)
func TestGetRedirctUrisLink(t *testing.T) {
html := []byte("<link rel=\"redirect_uri\" href=\"http://example.com/redirect\" />")
parser := &owl.OwlHtmlParser{}
uris, err := parser.GetRedirctUris(constructResponse(html))
assertions.AssertNoError(t, err, "Unable to parse feed")
assertions.AssertArrayContains(t, uris, "http://example.com/redirect")
}
func TestGetRedirctUrisLinkMultiple(t *testing.T) {
html := []byte(`
<link rel="redirect_uri" href="http://example.com/redirect1" />
<link rel="redirect_uri" href="http://example.com/redirect2" />
<link rel="redirect_uri" href="http://example.com/redirect3" />
<link rel="foo" href="http://example.com/redirect4" />
<link href="http://example.com/redirect5" />
`)
parser := &owl.OwlHtmlParser{}
uris, err := parser.GetRedirctUris(constructResponse(html))
assertions.AssertNoError(t, err, "Unable to parse feed")
assertions.AssertArrayContains(t, uris, "http://example.com/redirect1")
assertions.AssertArrayContains(t, uris, "http://example.com/redirect2")
assertions.AssertArrayContains(t, uris, "http://example.com/redirect3")
assertions.AssertLen(t, uris, 3)
}
func TestGetRedirectUrisLinkHeader(t *testing.T) {
html := []byte("")
parser := &owl.OwlHtmlParser{}
resp := constructResponse(html)
resp.Header = http.Header{"Link": []string{"<http://example.com/redirect>; rel=\"redirect_uri\""}}
uris, err := parser.GetRedirctUris(resp)
assertions.AssertNoError(t, err, "Unable to parse feed")
assertions.AssertArrayContains(t, uris, "http://example.com/redirect")
}

View File

@ -1,38 +0,0 @@
package main
import (
"h4kor/owl-blogs"
"github.com/spf13/cobra"
)
var domain string
var singleUser string
var unsafe bool
func init() {
rootCmd.AddCommand(initCmd)
initCmd.PersistentFlags().StringVar(&domain, "domain", "http://localhost:8080", "Domain to use")
initCmd.PersistentFlags().StringVar(&singleUser, "single-user", "", "Use single user mode with given username")
initCmd.PersistentFlags().BoolVar(&unsafe, "unsafe", false, "Allow raw html")
}
var initCmd = &cobra.Command{
Use: "init",
Short: "Creates a new repository",
Long: `Creates a new repository`,
Run: func(cmd *cobra.Command, args []string) {
_, err := owl.CreateRepository(repoPath, owl.RepoConfig{
Domain: domain,
SingleUser: singleUser,
AllowRawHtml: unsafe,
})
if err != nil {
println("Error creating repository: ", err.Error())
} else {
println("Repository created: ", repoPath)
}
},
}

View File

@ -1,32 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var repoPath string
var rootCmd = &cobra.Command{
Use: "owl",
Short: "Owl Blogs is a not so static blog generator",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
rootCmd.PersistentFlags().StringVar(&repoPath, "repo", ".", "Path to the repository to use.")
rootCmd.PersistentFlags().StringVar(&user, "user", "", "Username. Required for some commands.")
}
func main() {
Execute()
}

View File

@ -1,51 +0,0 @@
package main
import (
"h4kor/owl-blogs"
"github.com/spf13/cobra"
)
var postTitle string
func init() {
rootCmd.AddCommand(newPostCmd)
newPostCmd.PersistentFlags().StringVar(&postTitle, "title", "", "Post title")
}
var newPostCmd = &cobra.Command{
Use: "new-post",
Short: "Creates a new post",
Long: `Creates a new post`,
Run: func(cmd *cobra.Command, args []string) {
if user == "" {
println("Username is required")
return
}
if postTitle == "" {
println("Post title is required")
return
}
repo, err := owl.OpenRepository(repoPath)
if err != nil {
println("Error opening repository: ", err.Error())
return
}
user, err := repo.GetUser(user)
if err != nil {
println("Error getting user: ", err.Error())
return
}
post, err := user.CreateNewPost(owl.PostMeta{Type: "article", Title: postTitle, Draft: true}, "")
if err != nil {
println("Error creating post: ", err.Error())
} else {
println("Post created: ", postTitle)
println("Edit: ", post.ContentFile())
}
},
}

View File

@ -1,38 +0,0 @@
package main
import (
"h4kor/owl-blogs"
"github.com/spf13/cobra"
)
var user string
func init() {
rootCmd.AddCommand(newUserCmd)
}
var newUserCmd = &cobra.Command{
Use: "new-user",
Short: "Creates a new user",
Long: `Creates a new user`,
Run: func(cmd *cobra.Command, args []string) {
if user == "" {
println("Username is required")
return
}
repo, err := owl.OpenRepository(repoPath)
if err != nil {
println("Error opening repository: ", err.Error())
return
}
_, err = repo.CreateUser(user)
if err != nil {
println("Error creating user: ", err.Error())
} else {
println("User created: ", user)
}
},
}

View File

@ -1,44 +0,0 @@
package main
import (
"fmt"
"h4kor/owl-blogs"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(resetPasswordCmd)
}
var resetPasswordCmd = &cobra.Command{
Use: "reset-password",
Short: "Reset the password for a user",
Long: `Reset the password for a user`,
Run: func(cmd *cobra.Command, args []string) {
if user == "" {
println("Username is required")
return
}
repo, err := owl.OpenRepository(repoPath)
if err != nil {
println("Error opening repository: ", err.Error())
return
}
user, err := repo.GetUser(user)
if err != nil {
println("Error getting user: ", err.Error())
return
}
// generate a random password and print it
password := owl.GenerateRandomString(16)
user.ResetPassword(password)
fmt.Println("User: ", user.Name())
fmt.Println("New Password: ", password)
},
}

View File

@ -1,24 +0,0 @@
package main
import (
web "h4kor/owl-blogs/cmd/owl/web"
"github.com/spf13/cobra"
)
var port int
func init() {
rootCmd.AddCommand(webCmd)
webCmd.PersistentFlags().IntVar(&port, "port", 8080, "Port to use")
}
var webCmd = &cobra.Command{
Use: "web",
Short: "Start the web server",
Long: `Start the web server`,
Run: func(cmd *cobra.Command, args []string) {
web.StartServer(repoPath, port)
},
}

View File

@ -1,191 +0,0 @@
package web_test
import (
"h4kor/owl-blogs"
main "h4kor/owl-blogs/cmd/owl/web"
"h4kor/owl-blogs/test/assertions"
"net/http"
"net/http/httptest"
"os"
"testing"
)
func TestRedirectOnAliases(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - /foo/bar\n"
content += " - /foo/baz\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", "/foo/bar", nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusMovedPermanently)
// Check that Location header is set correctly
assertions.AssertEqual(t, rr.Header().Get("Location"), post.UrlPath())
}
func TestNoRedirectOnNonExistingAliases(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - /foo/bar\n"
content += " - /foo/baz\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", "/foo/bar2", nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusNotFound)
}
func TestNoRedirectIfValidPostUrl(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
post2, _ := user.CreateNewPost(owl.PostMeta{Title: "post-2"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - " + post2.UrlPath() + "\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", post2.UrlPath(), nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusOK)
}
func TestRedirectIfInvalidPostUrl(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - " + user.UrlPath() + "posts/not-a-real-post/" + "\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", user.UrlPath()+"posts/not-a-real-post/", nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusMovedPermanently)
}
func TestRedirectIfInvalidUserUrl(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - /user/not-real/ \n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", "/user/not-real/", nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusMovedPermanently)
}
func TestRedirectIfInvalidMediaUrl(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Test\n"
content += "aliases: \n"
content += " - " + post.UrlMediaPath("not-real") + "\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", post.UrlMediaPath("not-real"), nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.Router(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusMovedPermanently)
}
func TestDeepAliasInSingleUserMode(t *testing.T) {
repo := getTestRepo(owl.RepoConfig{SingleUser: "test-1"})
user, _ := repo.CreateUser("test-1")
post, _ := user.CreateNewPost(owl.PostMeta{Title: "post-1"}, "")
content := "---\n"
content += "title: Create tileable textures with GIMP\n"
content += "author: h4kor\n"
content += "type: post\n"
content += "date: Tue, 13 Sep 2016 16:19:09 +0000\n"
content += "aliases:\n"
content += " - /2016/09/13/create-tileable-textures-with-gimp/\n"
content += "categories:\n"
content += " - GameDev\n"
content += "tags:\n"
content += " - gamedev\n"
content += " - textures\n"
content += "---\n"
content += "This is a test"
os.WriteFile(post.ContentFile(), []byte(content), 0644)
// Create Request and Response
req, err := http.NewRequest("GET", "/2016/09/13/create-tileable-textures-with-gimp/", nil)
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusMovedPermanently)
}

View File

@ -1,396 +0,0 @@
package web
import (
"encoding/json"
"fmt"
"h4kor/owl-blogs"
"net/http"
"net/url"
"strings"
"github.com/julienschmidt/httprouter"
)
type IndieauthMetaDataResponse struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
ScopesSupported []string `json:"scopes_supported"`
ResponseTypesSupported []string `json:"response_types_supported"`
GrantTypesSupported []string `json:"grant_types_supported"`
}
type MeProfileResponse struct {
Name string `json:"name"`
Url string `json:"url"`
Photo string `json:"photo"`
}
type MeResponse struct {
Me string `json:"me"`
Profile MeProfileResponse `json:"profile"`
}
type AccessTokenResponse struct {
Me string `json:"me"`
TokenType string `json:"token_type"`
AccessToken string `json:"access_token"`
Scope string `json:"scope"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
}
func jsonResponse(w http.ResponseWriter, response interface{}) {
jsonData, err := json.Marshal(response)
if err != nil {
println("Error marshalling json: ", err.Error())
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal server error"))
}
w.Header().Add("Content-Type", "application/json")
w.Write(jsonData)
}
func userAuthMetadataHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user, err := getUserFromRepo(repo, ps)
if err != nil {
println("Error getting user: ", err.Error())
notFoundHandler(repo)(w, r)
return
}
w.WriteHeader(http.StatusOK)
jsonResponse(w, IndieauthMetaDataResponse{
Issuer: user.FullUrl(),
AuthorizationEndpoint: user.AuthUrl(),
TokenEndpoint: user.TokenUrl(),
CodeChallengeMethodsSupported: []string{"S256", "plain"},
ScopesSupported: []string{"profile"},
ResponseTypesSupported: []string{"code"},
GrantTypesSupported: []string{"authorization_code"},
})
}
}
func userAuthHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user, err := getUserFromRepo(repo, ps)
if err != nil {
println("Error getting user: ", err.Error())
notFoundHandler(repo)(w, r)
return
}
// get me, cleint_id, redirect_uri, state and response_type from query
me := r.URL.Query().Get("me")
clientId := r.URL.Query().Get("client_id")
redirectUri := r.URL.Query().Get("redirect_uri")
state := r.URL.Query().Get("state")
responseType := r.URL.Query().Get("response_type")
codeChallenge := r.URL.Query().Get("code_challenge")
codeChallengeMethod := r.URL.Query().Get("code_challenge_method")
scope := r.URL.Query().Get("scope")
// check if request is valid
missing_params := []string{}
if clientId == "" {
missing_params = append(missing_params, "client_id")
}
if redirectUri == "" {
missing_params = append(missing_params, "redirect_uri")
}
if responseType == "" {
missing_params = append(missing_params, "response_type")
}
if len(missing_params) > 0 {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Missing parameters",
Message: "Missing parameters: " + strings.Join(missing_params, ", "),
})
w.Write([]byte(html))
return
}
if responseType == "id" {
responseType = "code"
}
if responseType != "code" {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Invalid response_type",
Message: "Must be 'code' ('id' converted to 'code' for legacy support).",
})
w.Write([]byte(html))
return
}
if codeChallengeMethod != "" && (codeChallengeMethod != "S256" && codeChallengeMethod != "plain") {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Invalid code_challenge_method",
Message: "Must be 'S256' or 'plain'.",
})
w.Write([]byte(html))
return
}
client_id_url, err := url.Parse(clientId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Invalid client_id",
Message: "Invalid client_id: " + clientId,
})
w.Write([]byte(html))
return
}
redirect_uri_url, err := url.Parse(redirectUri)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Invalid redirect_uri",
Message: "Invalid redirect_uri: " + redirectUri,
})
w.Write([]byte(html))
return
}
if client_id_url.Host != redirect_uri_url.Host || client_id_url.Scheme != redirect_uri_url.Scheme {
// check if redirect_uri is registered
resp, _ := repo.HttpClient.Get(clientId)
registered_redirects, _ := repo.Parser.GetRedirctUris(resp)
is_registered := false
for _, registered_redirect := range registered_redirects {
if registered_redirect == redirectUri {
// redirect_uri is registered
is_registered = true
break
}
}
if !is_registered {
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Invalid redirect_uri",
Message: redirectUri + " is not registered for " + clientId,
})
w.Write([]byte(html))
return
}
}
// Double Submit Cookie Pattern
// https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
csrfToken := owl.GenerateRandomString(32)
cookie := http.Cookie{
Name: "csrf_token",
Value: csrfToken,
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
}
http.SetCookie(w, &cookie)
reqData := owl.AuthRequestData{
Me: me,
ClientId: clientId,
RedirectUri: redirectUri,
State: state,
Scope: scope,
ResponseType: responseType,
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
User: user,
CsrfToken: csrfToken,
}
html, err := owl.RenderUserAuthPage(reqData)
if err != nil {
println("Error rendering auth page: ", err.Error())
w.WriteHeader(http.StatusInternalServerError)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Internal Server Error",
Message: "Internal Server Error",
})
w.Write([]byte(html))
return
}
w.Write([]byte(html))
}
}
func verifyAuthCodeRequest(user owl.User, w http.ResponseWriter, r *http.Request) (bool, owl.AuthCode) {
// get form data from post request
err := r.ParseForm()
if err != nil {
println("Error parsing form: ", err.Error())
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Error parsing form"))
return false, owl.AuthCode{}
}
code := r.Form.Get("code")
client_id := r.Form.Get("client_id")
redirect_uri := r.Form.Get("redirect_uri")
code_verifier := r.Form.Get("code_verifier")
// check if request is valid
valid, authCode := user.VerifyAuthCode(code, client_id, redirect_uri, code_verifier)
if !valid {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Invalid code"))
}
return valid, authCode
}
func userAuthProfileHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user, err := getUserFromRepo(repo, ps)
if err != nil {
println("Error getting user: ", err.Error())
notFoundHandler(repo)(w, r)
return
}
valid, _ := verifyAuthCodeRequest(user, w, r)
if valid {
w.WriteHeader(http.StatusOK)
jsonResponse(w, MeResponse{
Me: user.FullUrl(),
Profile: MeProfileResponse{
Name: user.Name(),
Url: user.FullUrl(),
Photo: user.AvatarUrl(),
},
})
return
}
}
}
func userAuthTokenHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user, err := getUserFromRepo(repo, ps)
if err != nil {
println("Error getting user: ", err.Error())
notFoundHandler(repo)(w, r)
return
}
valid, authCode := verifyAuthCodeRequest(user, w, r)
if valid {
if authCode.Scope == "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Empty scope, no token issued"))
return
}
accessToken, duration, err := user.GenerateAccessToken(authCode)
if err != nil {
println("Error generating access token: ", err.Error())
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal server error"))
return
}
jsonResponse(w, AccessTokenResponse{
Me: user.FullUrl(),
TokenType: "Bearer",
AccessToken: accessToken,
Scope: authCode.Scope,
ExpiresIn: duration,
})
return
}
}
}
func userAuthVerifyHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user, err := getUserFromRepo(repo, ps)
if err != nil {
println("Error getting user: ", err.Error())
notFoundHandler(repo)(w, r)
return
}
// get form data from post request
err = r.ParseForm()
if err != nil {
println("Error parsing form: ", err.Error())
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Error parsing form",
Message: "Error parsing form",
})
w.Write([]byte(html))
return
}
password := r.FormValue("password")
client_id := r.FormValue("client_id")
redirect_uri := r.FormValue("redirect_uri")
response_type := r.FormValue("response_type")
state := r.FormValue("state")
code_challenge := r.FormValue("code_challenge")
code_challenge_method := r.FormValue("code_challenge_method")
scope := r.FormValue("scope")
// CSRF check
formCsrfToken := r.FormValue("csrf_token")
cookieCsrfToken, err := r.Cookie("csrf_token")
if err != nil {
println("Error getting csrf token from cookie: ", err.Error())
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "CSRF Error",
Message: "Error getting csrf token from cookie",
})
w.Write([]byte(html))
return
}
if formCsrfToken != cookieCsrfToken.Value {
println("Invalid csrf token")
w.WriteHeader(http.StatusBadRequest)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "CSRF Error",
Message: "Invalid csrf token",
})
w.Write([]byte(html))
return
}
password_valid := user.VerifyPassword(password)
if !password_valid {
redirect := fmt.Sprintf(
"%s?error=invalid_password&client_id=%s&redirect_uri=%s&response_type=%s&state=%s",
user.AuthUrl(), client_id, redirect_uri, response_type, state,
)
if code_challenge != "" {
redirect += fmt.Sprintf("&code_challenge=%s&code_challenge_method=%s", code_challenge, code_challenge_method)
}
http.Redirect(w, r,
redirect,
http.StatusFound,
)
return
} else {
// password is valid, generate code
code, err := user.GenerateAuthCode(
client_id, redirect_uri, code_challenge, code_challenge_method, scope)
if err != nil {
println("Error generating code: ", err.Error())
w.WriteHeader(http.StatusInternalServerError)
html, _ := owl.RenderUserError(user, owl.ErrorMessage{
Error: "Internal Server Error",
Message: "Error generating code",
})
w.Write([]byte(html))
return
}
http.Redirect(w, r,
fmt.Sprintf(
"%s?code=%s&state=%s&iss=%s",
redirect_uri, code, state,
user.FullUrl(),
),
http.StatusFound,
)
return
}
}
}

View File

@ -1,428 +0,0 @@
package web_test
import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
main "h4kor/owl-blogs/cmd/owl/web"
"h4kor/owl-blogs/test/assertions"
"h4kor/owl-blogs/test/mocks"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"testing"
)
func TestAuthPostWrongPassword(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
csrfToken := "test_csrf_token"
// Create Request and Response
form := url.Values{}
form.Add("password", "wrongpassword")
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("response_type", "code")
form.Add("state", "test_state")
form.Add("csrf_token", csrfToken)
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusFound)
assertions.AssertContains(t, rr.Header().Get("Location"), "error=invalid_password")
}
func TestAuthPostCorrectPassword(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
csrfToken := "test_csrf_token"
// Create Request and Response
form := url.Values{}
form.Add("password", "testpassword")
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("response_type", "code")
form.Add("state", "test_state")
form.Add("csrf_token", csrfToken)
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusFound)
assertions.AssertContains(t, rr.Header().Get("Location"), "code=")
assertions.AssertContains(t, rr.Header().Get("Location"), "state=test_state")
assertions.AssertContains(t, rr.Header().Get("Location"), "iss="+user.FullUrl())
assertions.AssertContains(t, rr.Header().Get("Location"), "http://example.com/response")
}
func TestAuthPostWithIncorrectCode(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
user.GenerateAuthCode("http://example.com", "http://example.com/response", "", "", "profile")
// Create Request and Response
form := url.Values{}
form.Add("code", "wrongcode")
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusUnauthorized)
}
func TestAuthPostWithCorrectCode(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
code, _ := user.GenerateAuthCode("http://example.com", "http://example.com/response", "", "", "profile")
// Create Request and Response
form := url.Values{}
form.Add("code", code)
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.Header.Add("Accept", "application/json")
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusOK)
// parse response as json
type responseType struct {
Me string `json:"me"`
}
var response responseType
json.Unmarshal(rr.Body.Bytes(), &response)
assertions.AssertEqual(t, response.Me, user.FullUrl())
}
func TestAuthPostWithCorrectCodeAndPKCE(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
// Create Request and Response
code_verifier := "test_code_verifier"
// create code challenge
h := sha256.New()
h.Write([]byte(code_verifier))
code_challenge := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
code, _ := user.GenerateAuthCode("http://example.com", "http://example.com/response", code_challenge, "S256", "profile")
form := url.Values{}
form.Add("code", code)
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
form.Add("code_verifier", code_verifier)
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.Header.Add("Accept", "application/json")
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusOK)
// parse response as json
type responseType struct {
Me string `json:"me"`
}
var response responseType
json.Unmarshal(rr.Body.Bytes(), &response)
assertions.AssertEqual(t, response.Me, user.FullUrl())
}
func TestAuthPostWithCorrectCodeAndWrongPKCE(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
// Create Request and Response
code_verifier := "test_code_verifier"
// create code challenge
h := sha256.New()
h.Write([]byte(code_verifier + "wrong"))
code_challenge := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
code, _ := user.GenerateAuthCode("http://example.com", "http://example.com/response", code_challenge, "S256", "profile")
form := url.Values{}
form.Add("code", code)
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
form.Add("code_verifier", code_verifier)
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.Header.Add("Accept", "application/json")
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusUnauthorized)
}
func TestAuthPostWithCorrectCodePKCEPlain(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
// Create Request and Response
code_verifier := "test_code_verifier"
code_challenge := code_verifier
code, _ := user.GenerateAuthCode("http://example.com", "http://example.com/response", code_challenge, "plain", "profile")
form := url.Values{}
form.Add("code", code)
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
form.Add("code_verifier", code_verifier)
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.Header.Add("Accept", "application/json")
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusOK)
}
func TestAuthPostWithCorrectCodePKCEPlainWrong(t *testing.T) {
repo, user := getSingleUserTestRepo()
user.ResetPassword("testpassword")
// Create Request and Response
code_verifier := "test_code_verifier"
code_challenge := code_verifier + "wrong"
code, _ := user.GenerateAuthCode("http://example.com", "http://example.com/response", code_challenge, "plain", "profile")
form := url.Values{}
form.Add("code", code)
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("grant_type", "authorization_code")
form.Add("code_verifier", code_verifier)
req, err := http.NewRequest("POST", user.AuthUrl(), strings.NewReader(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.Header.Add("Accept", "application/json")
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusUnauthorized)
}
func TestAuthRedirectUriNotSet(t *testing.T) {
repo, user := getSingleUserTestRepo()
repo.HttpClient = &mocks.MockHttpClient{}
repo.Parser = &mocks.MockParseLinksHtmlParser{
Links: []string{"http://example2.com/response"},
}
user.ResetPassword("testpassword")
csrfToken := "test_csrf_token"
// Create Request and Response
form := url.Values{}
form.Add("password", "wrongpassword")
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example2.com/response_not_set")
form.Add("response_type", "code")
form.Add("state", "test_state")
form.Add("csrf_token", csrfToken)
req, err := http.NewRequest("GET", user.AuthUrl()+"?"+form.Encode(), nil)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusBadRequest)
}
func TestAuthRedirectUriSet(t *testing.T) {
repo, user := getSingleUserTestRepo()
repo.HttpClient = &mocks.MockHttpClient{}
repo.Parser = &mocks.MockParseLinksHtmlParser{
Links: []string{"http://example.com/response"},
}
user.ResetPassword("testpassword")
csrfToken := "test_csrf_token"
// Create Request and Response
form := url.Values{}
form.Add("password", "wrongpassword")
form.Add("client_id", "http://example.com")
form.Add("redirect_uri", "http://example.com/response")
form.Add("response_type", "code")
form.Add("state", "test_state")
form.Add("csrf_token", csrfToken)
req, err := http.NewRequest("GET", user.AuthUrl()+"?"+form.Encode(), nil)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
assertions.AssertNoError(t, err, "Error creating request")
rr := httptest.NewRecorder()
router := main.SingleUserRouter(&repo)
router.ServeHTTP(rr, req)
assertions.AssertStatus(t, rr, http.StatusOK)
}
func TestAuthRedirectUriSameHost(t *testing.T) {