A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 161 lines 3.7 kB view raw
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}