/
handler.go
153 lines (126 loc) · 3.61 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package webserver
import (
"fmt"
"html/template"
"net/http"
"strings"
"github.com/go-shiori/shiori/internal/config"
"github.com/go-shiori/shiori/internal/database"
"github.com/go-shiori/shiori/internal/model"
cch "github.com/patrickmn/go-cache"
"github.com/sirupsen/logrus"
)
var developmentMode = false
// Handler is Handler for serving the web interface.
type Handler struct {
DB database.DB
DataDir string
RootPath string
UserCache *cch.Cache
SessionCache *cch.Cache
ArchiveCache *cch.Cache
Log bool
dependencies *config.Dependencies
templates map[string]*template.Template
}
func (h *Handler) PrepareSessionCache() {
h.SessionCache.OnEvicted(func(key string, val interface{}) {
account := val.(model.Account)
arr, found := h.UserCache.Get(account.Username)
if !found {
return
}
sessionIDs := arr.([]string)
for i := 0; i < len(sessionIDs); i++ {
if sessionIDs[i] == key {
sessionIDs = append(sessionIDs[:i], sessionIDs[i+1:]...)
break
}
}
h.UserCache.Set(account.Username, sessionIDs, -1)
})
}
func (h *Handler) PrepareTemplates() error {
// Prepare variables
var err error
h.templates = make(map[string]*template.Template)
// Prepare func map
funcMap := template.FuncMap{
"html": func(s string) template.HTML {
return template.HTML(s)
},
}
// Create template for login, index and content
for _, name := range []string{"login", "index", "content"} {
h.templates[name], err = createTemplate(name+".html", funcMap)
if err != nil {
return err
}
}
// Create template for archive overlay
h.templates["archive"], err = template.New("archive").Delims("$$", "$$").Parse(
`<div id="shiori-archive-header">
<p id="shiori-logo"><span>栞</span>shiori</p>
<div class="spacer"></div>
<a href="$$.URL$$" target="_blank">View Original</a>
$$if .HasContent$$
<a href="/bookmark/$$.ID$$/content">View Readable</a>
$$end$$
</div>`)
if err != nil {
return err
}
return nil
}
func (h *Handler) GetSessionID(r *http.Request) string {
// Try to get session ID from the header
sessionID := r.Header.Get("X-Session-Id")
// If not, try it from the cookie
if sessionID == "" {
cookie, err := r.Cookie("session-id")
if err != nil {
return ""
}
sessionID = cookie.Value
}
return sessionID
}
// validateSession checks whether user session is still valid or not
func (h *Handler) validateSession(r *http.Request) error {
authorization := r.Header.Get(model.AuthorizationHeader)
if authorization != "" {
authParts := strings.SplitN(authorization, " ", 2)
if len(authParts) != 2 && authParts[0] != model.AuthorizationTokenType {
return fmt.Errorf("session has been expired")
}
account, err := h.dependencies.Domains.Auth.CheckToken(r.Context(), authParts[1])
if err != nil {
return fmt.Errorf("session has been expired")
}
if r.Method != "" && r.Method != "GET" && !account.Owner {
return fmt.Errorf("account level is not sufficient")
}
h.dependencies.Log.WithFields(logrus.Fields{
"username": account.Username,
"method": r.Method,
"path": r.URL.Path,
}).Info("allowing legacy api access using JWT token")
return nil
}
sessionID := h.GetSessionID(r)
if sessionID == "" {
return fmt.Errorf("session is not exist")
}
// Make sure session is not expired yet
val, found := h.SessionCache.Get(sessionID)
if !found {
return fmt.Errorf("session has been expired")
}
// If this is not get request, make sure it's owner
if r.Method != "" && r.Method != "GET" {
if account := val.(model.Account); !account.Owner {
return fmt.Errorf("account level is not sufficient")
}
}
return nil
}