207 lines
5.2 KiB
Go
207 lines
5.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"net/http"
|
|
"os"
|
|
"ripper/internal/app/github_auth"
|
|
"ripper/internal/response"
|
|
jwtpkg "ripper/pkg/jwt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type OAuthCheck struct {
|
|
ClientId string `json:"client_id" form:"client_id"`
|
|
DeviceCode string `json:"device_code" form:"device_code"`
|
|
GrantType string `json:"grant_type" form:"grant_type"`
|
|
}
|
|
|
|
func DeviceCodeCheckAuth(ctx *gin.Context) {
|
|
checkInfo := &OAuthCheck{}
|
|
if err := ctx.ShouldBind(&checkInfo); err != nil {
|
|
response.FailJson(ctx, response.FailStruct{
|
|
Code: -1,
|
|
Msg: "Invalid client id.",
|
|
}, false)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
info, _ := github_auth.GetClientAuthInfoByDeviceCode(checkInfo.DeviceCode)
|
|
if info.CardCode == "" {
|
|
ctx.JSON(http.StatusOK, gin.H{
|
|
"error": "authorization_pending",
|
|
"error_description": "The authorization request is still pending.",
|
|
"error_uri": "https://docs.github.com/developers/apps/authorizing-oauth-apps#error-codes-for-the-device-flow",
|
|
})
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
ctx.Set("client_auth_info", info)
|
|
ctx.Next()
|
|
}
|
|
|
|
func AuthCodeFlowCheckAuth(ctx *gin.Context) {
|
|
checkInfoClient := &github_auth.ClientOAuthInfo{}
|
|
err := ctx.Bind(&checkInfoClient)
|
|
if err != nil {
|
|
response.FailJson(ctx, response.FailStruct{
|
|
Code: -1,
|
|
Msg: "Invalid client id.",
|
|
}, false)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
oauthCodeInfo, err := github_auth.GetOAuthCodeInfoByClientIdAndCode(checkInfoClient.ClientId, checkInfoClient.Code)
|
|
if err != nil {
|
|
response.FailJson(ctx, response.FailStruct{
|
|
Code: -1,
|
|
Msg: "Invalid client id.",
|
|
}, false)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
|
|
ctx.Set("client_auth_info", oauthCodeInfo)
|
|
ctx.Next()
|
|
}
|
|
|
|
func AccessTokenCheckAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
token := c.Request.Header.Get("Authorization")
|
|
if token == "" {
|
|
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.NoAccess, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
last := strings.Index(token, " ")
|
|
if len(token) < last || last == -1 {
|
|
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.TokenWrongful, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
token = token[last+1:]
|
|
chk, jwter, err := jwtpkg.CheckToken(token, &UserLoad{}, "user")
|
|
if err != nil {
|
|
errmsg := response.TokenWrongful
|
|
errmsg.Msg = "令牌验证错误"
|
|
response.FailJsonAndStatusCode(c, http.StatusForbidden, errmsg, true, err.Error())
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !chk {
|
|
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.NoAccess, true, "破损令牌")
|
|
c.Abort()
|
|
return
|
|
}
|
|
chs := true
|
|
issuerStr := ""
|
|
issuerStr, err = jwter.GetIssuer()
|
|
if err != nil {
|
|
chs = false
|
|
c.Abort()
|
|
return
|
|
}
|
|
if "user" != issuerStr && issuerStr != "" {
|
|
chs = false
|
|
c.Abort()
|
|
return
|
|
}
|
|
if !chs {
|
|
errmsg := response.TokenWrongful
|
|
errmsg.Msg = "签名错误"
|
|
response.FailJsonAndStatusCode(c, http.StatusForbidden, errmsg, true, err.Error())
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Set("token", jwter)
|
|
c.Set("tokenStr", token)
|
|
c.Set("token.issuer", issuerStr)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func TokenCheckAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
clientType := os.Getenv("COPILOT_CLIENT_TYPE")
|
|
copilotProxyAll, err := strconv.ParseBool(os.Getenv("COPILOT_PROXY_ALL"))
|
|
if clientType == "github" && !copilotProxyAll {
|
|
c.Next()
|
|
return
|
|
}
|
|
token := c.Request.Header.Get("Authorization")
|
|
if token == "" {
|
|
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
last := strings.Index(token, " ")
|
|
if len(token) < last || last == -1 {
|
|
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
token = token[last+1:]
|
|
parsedToken := parseAuthorizationToken(token)
|
|
// 校验exp是否过期
|
|
expired, err := isExpired(parsedToken["exp"])
|
|
if err != nil {
|
|
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
|
c.Abort()
|
|
return
|
|
} else {
|
|
if expired {
|
|
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenOverdue, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
}
|
|
rawToken := github_auth.JsonMap2Token(map[string]interface{}{
|
|
"tid": parsedToken["tid"],
|
|
"exp": parsedToken["exp"],
|
|
"sku": parsedToken["sku"],
|
|
"st": parsedToken["st"],
|
|
"chat": parsedToken["chat"],
|
|
"u": parsedToken["u"],
|
|
})
|
|
sign := "1:" + github_auth.Token2Sign(rawToken)
|
|
if sign != parsedToken["8kp"] {
|
|
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func parseAuthorizationToken(token string) map[string]string {
|
|
result := make(map[string]string)
|
|
pairs := strings.Split(token, ";")
|
|
|
|
for _, pair := range pairs {
|
|
kv := strings.SplitN(pair, "=", 2)
|
|
if len(kv) == 2 {
|
|
key := kv[0]
|
|
value := kv[1]
|
|
|
|
if key == "tid" || key == "exp" || key == "sku" || key == "st" || key == "8kp" || key == "chat" || key == "u" {
|
|
result[key] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func isExpired(expStr string) (bool, error) {
|
|
exp, err := strconv.ParseInt(expStr, 10, 64)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid exp timestamp: %v", err)
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
return now > exp, nil
|
|
}
|