This commit is contained in:
Бородин Роман 2023-07-31 12:46:18 +03:00
parent f6a72ff464
commit 35ac5a50d6
7 changed files with 172 additions and 12 deletions

View File

@ -18,7 +18,6 @@ package conf
import ( import (
"encoding/json" "encoding/json"
"html/template" "html/template"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -66,6 +65,11 @@ type conf struct {
Autocomplete bool // default autocomplete Autocomplete bool // default autocomplete
SiteStatCode template.HTML // site statistic code SiteStatCode template.HTML // site statistic code
ReadOnly bool // read-only mode ReadOnly bool // read-only mode
OAuthLoginURL string
OAuthAccessTokenURL string
OAuthUserInfoURL string
OAuthClientID string
OAuthClientSecret string
} }
// Logger. // Logger.
@ -124,7 +128,7 @@ func initUsers() {
user := &User{} user := &User{}
bytes, _ := ioutil.ReadFile(filepath.Join(Wide.Data, "users", name)) bytes, _ := os.ReadFile(filepath.Join(Wide.Data, "users", name))
err := json.Unmarshal(bytes, user) err := json.Unmarshal(bytes, user)
if err != nil { if err != nil {
logger.Errorf("Parses [%s] error: %v, skip loading this user", name, err) logger.Errorf("Parses [%s] error: %v, skip loading this user", name, err)
@ -156,7 +160,7 @@ func initUsers() {
} }
func initWide(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) { func initWide(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) {
bytes, err := ioutil.ReadFile(confPath) bytes, err := os.ReadFile(confPath)
if nil != err { if nil != err {
logger.Error(err) logger.Error(err)

View File

@ -7,5 +7,10 @@
"StaticResourceVersion": "${time}", "StaticResourceVersion": "${time}",
"Locale": "zh_CN", "Locale": "zh_CN",
"SiteStatCode": "", "SiteStatCode": "",
"ReadOnly": false "ReadOnly": false,
"OAuthLoginURL": "",
"OAuthAccessTokenURL": "",
"OAuthUserInfoURL": "",
"OAuthClientID": "",
"OAuthClientSecret": ""
} }

10
go.mod
View File

@ -1,17 +1,23 @@
module github.com/88250/wide module github.com/88250/wide
go 1.12 go 1.20
require ( require (
github.com/88250/gulu v1.1.0 github.com/88250/gulu v1.1.0
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 // indirect
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/gorilla/sessions v1.2.0 github.com/gorilla/sessions v1.2.0
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/parnurzeal/gorequest v0.2.16 github.com/parnurzeal/gorequest v0.2.16
)
require (
github.com/davidebianchi/go-jsonclient v1.5.0 // indirect
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b // indirect golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b // indirect
golang.org/x/text v0.3.2 // indirect
moul.io/http2curl v1.0.0 // indirect moul.io/http2curl v1.0.0 // indirect
) )

4
go.sum
View File

@ -1,8 +1,9 @@
github.com/88250/gulu v1.1.0 h1:aU8HncW1XNssT1insCOz6rvmTUDrtsDwSnzeqTEqNFA= github.com/88250/gulu v1.1.0 h1:aU8HncW1XNssT1insCOz6rvmTUDrtsDwSnzeqTEqNFA=
github.com/88250/gulu v1.1.0/go.mod h1:a2POIziN+QFeNT4Mj7FHH2lz1HEaFlMRF6wPE0NHM4U= github.com/88250/gulu v1.1.0/go.mod h1:a2POIziN+QFeNT4Mj7FHH2lz1HEaFlMRF6wPE0NHM4U=
github.com/davidebianchi/go-jsonclient v1.5.0 h1:PVDunAF/6c30D2SSx711efsrUP3hhml+uGT6oD3lzVY=
github.com/davidebianchi/go-jsonclient v1.5.0/go.mod h1:lXd/hkx23H590Dod74j5GsmpgxF8RIAGZDt1P5+2mqo=
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 h1:TEmChtx8+IeOghiySC8kQIr0JZOdKUmRmmkuRDuYs3E= github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 h1:TEmChtx8+IeOghiySC8kQIr0JZOdKUmRmmkuRDuYs3E=
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@ -31,7 +32,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk= golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -15,6 +15,7 @@
package session package session
import ( import (
"fmt"
"html/template" "html/template"
"math/rand" "math/rand"
"net/http" "net/http"
@ -34,11 +35,12 @@ var states = map[string]string{}
// LoginRedirectHandler redirects to HacPai auth page. // LoginRedirectHandler redirects to HacPai auth page.
func LoginRedirectHandler(w http.ResponseWriter, r *http.Request) { func LoginRedirectHandler(w http.ResponseWriter, r *http.Request) {
loginAuthURL := "https://ld246.com/login?goto=" + conf.Wide.Server + "/login/callback" loginAuthURL := conf.Wide.OAuthLoginURL + "?response_type=code&redirect_uri=" + conf.Wide.Server + "/login/callback"
// надо будет добавить ttlcache для state и проверять для предотвращения атак CSRF
state := gulu.Rand.String(16) state := gulu.Rand.String(16)
states[state] = state states[state] = state
path := loginAuthURL + "&state=" + state + "&v=" + conf.WideVersion path := loginAuthURL + "&state=" + state + "&client_id=" + conf.Wide.OAuthClientID
http.Redirect(w, r, path, http.StatusSeeOther) http.Redirect(w, r, path, http.StatusSeeOther)
} }
@ -51,8 +53,25 @@ func LoginCallbackHandler(w http.ResponseWriter, r *http.Request) {
} }
delete(states, state) delete(states, state)
accessToken := r.URL.Query().Get("access_token") code := r.URL.Query().Get("code")
userInfo := util.HacPaiUserInfo(accessToken) accessToken, err := util.GetOAuthToken(
conf.Wide.OAuthAccessTokenURL,
conf.Wide.OAuthClientID,
conf.Wide.OAuthClientSecret,
code,
conf.Wide.Server+`/login/callback`)
if err != nil {
http.Error(w, fmt.Sprintf(`get access_token failed. error: %s`, err.Error()), http.StatusBadRequest)
return
}
userInfo, err := util.OpenIdUserInfo(conf.Wide.OAuthUserInfoURL, accessToken)
if err != nil {
http.Error(w, fmt.Sprintf(`get user_info failed. error: %s`, err.Error()), http.StatusBadRequest)
return
}
userId := userInfo["userId"].(string) userId := userInfo["userId"].(string)
userName := userInfo["userName"].(string) userName := userInfo["userName"].(string)

65
util/oauth_token.go Normal file
View File

@ -0,0 +1,65 @@
package util
import (
"fmt"
"github.com/davidebianchi/go-jsonclient"
"net/http"
"net/url"
)
type oAuthAccessTokenReq struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Code string `json:"code"`
GrantType string `json:"grant_type"`
RedirectURI string `json:"redirect_uri"`
}
type oAuthAccessTokenResp struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
}
func GetOAuthToken(oAuthAccessTokenURL, clientID, clientSecret, code, redirectURI string) (string, error) {
u, err := url.Parse(oAuthAccessTokenURL)
if err != nil {
logger.Errorf(`failed to parse oAuthAccessTokenURL. error: %s`, err.Error())
return ``, err
}
client, err := jsonclient.New(jsonclient.Options{
BaseURL: fmt.Sprintf(`%s://%s/`, u.Scheme, u.Host),
})
if err != nil {
logger.Errorf(`failed to create access_token client. error: %s`, err.Error())
return ``, err
}
req, err := client.NewRequest(http.MethodPost, u.Path, &oAuthAccessTokenReq{
ClientID: clientID,
ClientSecret: clientSecret,
Code: code,
GrantType: `authorization_code`,
RedirectURI: redirectURI,
})
if err != nil {
logger.Errorf(`failed to create access_token request. error: %s`, err.Error())
return ``, err
}
atResp := new(oAuthAccessTokenResp)
resp, err := client.Do(req, atResp)
if err != nil {
logger.Errorf(`failed to request access_token. error: %s`, err.Error())
return ``, err
}
if resp.StatusCode >= 300 {
logger.Errorf(`access_token request return wrong code. code: %d, err_msg: %s`, resp.StatusCode, resp.Status)
return ``, err
}
return atResp.AccessToken, nil
}

61
util/open_id.go Normal file
View File

@ -0,0 +1,61 @@
package util
import (
"fmt"
"github.com/davidebianchi/go-jsonclient"
"net/http"
"net/url"
)
type openIdUserInfoResp struct {
Name string `json:"name"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
PreferredUsername string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
}
func OpenIdUserInfo(openIdUserInfoURL, accessToken string) (map[string]any, error) {
u, err := url.Parse(openIdUserInfoURL)
if err != nil {
logger.Errorf(`failed to parse openIdUserInfoURL. error: %s`, err.Error())
return nil, err
}
client, err := jsonclient.New(jsonclient.Options{
BaseURL: fmt.Sprintf(`%s://%s/`, u.Scheme, u.Host),
Headers: map[string]string{
`Authorization`: fmt.Sprintf(`Bearer %s`, accessToken),
},
})
if err != nil {
logger.Errorf(`failed to create user_info client. error: %s`, err.Error())
return nil, err
}
req, err := client.NewRequest(http.MethodGet, u.Path, nil)
if err != nil {
logger.Errorf(`failed to create user_info request. error: %s`, err.Error())
return nil, err
}
uiResp := new(openIdUserInfoResp)
resp, err := client.Do(req, uiResp)
if err != nil {
logger.Errorf(`failed to request user_info. error: %s`, err.Error())
return nil, err
}
if resp.StatusCode >= 300 {
logger.Errorf(`user_info request return wrong code. code: %d, err_msg: %s`, resp.StatusCode, resp.Status)
return nil, err
}
return map[string]any{
`userId`: uiResp.PreferredUsername,
`userName`: uiResp.Name,
`avatar`: uiResp.Picture,
}, nil
}