A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
1// SiYuan - Refactor your thinking
2// Copyright (c) 2020-present, b3log.org
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17package model
18
19import (
20 "crypto/rand"
21 "net/http"
22 "sync"
23
24 "github.com/golang-jwt/jwt/v5"
25 "github.com/google/uuid"
26 "github.com/siyuan-note/logging"
27)
28
29type Account struct {
30 Username string
31 Password string
32 Token string
33}
34type AccountsMap map[string]*Account // username -> account
35type SessionsMap map[string]string // sessionID -> username
36type ClaimsKeyType string
37
38const (
39 XAuthTokenKey = "X-Auth-Token"
40
41 SessionIdCookieName = "publish-visitor-session-id"
42
43 ClaimsContextKey = "claims"
44
45 iss = "siyuan-publish-reverse-proxy-server"
46 sub = "publish"
47 aud = "siyuan-kernel"
48
49 ClaimsKeyRole string = "role"
50)
51
52var (
53 accountsMap = AccountsMap{}
54 sessionsMap = SessionsMap{}
55 sessionLock = sync.Mutex{}
56
57 jwtKey = make([]byte, 32)
58)
59
60func GetBasicAuthAccount(username string) *Account {
61 return accountsMap[username]
62}
63
64func GetBasicAuthUsernameBySessionID(sessionID string) string {
65 return sessionsMap[sessionID]
66}
67
68func GetNewSessionID() string {
69 sessionID := uuid.New().String()
70 return sessionID
71}
72
73func AddSession(sessionID, username string) {
74 sessionLock.Lock()
75 defer sessionLock.Unlock()
76 sessionsMap[sessionID] = username
77}
78
79func DeleteSession(sessionID string) {
80 sessionLock.Lock()
81 defer sessionLock.Unlock()
82 delete(sessionsMap, sessionID)
83}
84
85func InitAccounts() {
86 accountsMap = AccountsMap{
87 "": &Account{}, // 匿名用户
88 }
89 for _, account := range Conf.Publish.Auth.Accounts {
90 accountsMap[account.Username] = &Account{
91 Username: account.Username,
92 Password: account.Password,
93 }
94 }
95
96 InitJWT()
97}
98
99func InitJWT() {
100 if _, err := rand.Read(jwtKey); err != nil {
101 logging.LogErrorf("generate JWT signing key failed: %s", err)
102 return
103 }
104
105 for username, account := range accountsMap {
106 // REF: https://golang-jwt.github.io/jwt/usage/create/
107 t := jwt.NewWithClaims(
108 jwt.SigningMethodHS256,
109 jwt.MapClaims{
110 "iss": iss,
111 "sub": sub,
112 "aud": aud,
113 "jti": username,
114
115 ClaimsKeyRole: RoleReader,
116 },
117 )
118 if token, err := t.SignedString(jwtKey); err != nil {
119 logging.LogErrorf("JWT signature failed: %s", err)
120 return
121 } else {
122 account.Token = token
123 }
124 }
125}
126
127func ParseJWT(tokenString string) (*jwt.Token, error) {
128 // REF: https://golang-jwt.github.io/jwt/usage/parse/
129 return jwt.Parse(
130 tokenString,
131 func(token *jwt.Token) (interface{}, error) {
132 return jwtKey, nil
133 },
134 jwt.WithIssuer(iss),
135 jwt.WithSubject(sub),
136 jwt.WithAudience(aud),
137 )
138}
139
140func ParseXAuthToken(r *http.Request) *jwt.Token {
141 tokenString := r.Header.Get(XAuthTokenKey)
142 if tokenString != "" {
143 if token, err := ParseJWT(tokenString); err != nil {
144 logging.LogErrorf("JWT parse failed: %s", err)
145 } else {
146 return token
147 }
148 }
149 return nil
150}
151
152func GetTokenClaims(token *jwt.Token) jwt.MapClaims {
153 return token.Claims.(jwt.MapClaims)
154}
155
156func GetClaimRole(claims jwt.MapClaims) Role {
157 if role := claims[ClaimsKeyRole]; role != nil {
158 return Role(role.(float64))
159 }
160 return RoleVisitor
161}