| | package controller
|
| |
|
| | import (
|
| | "encoding/base64"
|
| | "encoding/json"
|
| | "errors"
|
| | "fmt"
|
| | "net/http"
|
| | "net/url"
|
| | "strconv"
|
| | "strings"
|
| | "time"
|
| |
|
| | "github.com/QuantumNous/new-api/common"
|
| | "github.com/QuantumNous/new-api/model"
|
| |
|
| | "github.com/gin-contrib/sessions"
|
| | "github.com/gin-gonic/gin"
|
| | )
|
| |
|
| | type LinuxdoUser struct {
|
| | Id int `json:"id"`
|
| | Username string `json:"username"`
|
| | Name string `json:"name"`
|
| | Active bool `json:"active"`
|
| | TrustLevel int `json:"trust_level"`
|
| | Silenced bool `json:"silenced"`
|
| | }
|
| |
|
| | func LinuxDoBind(c *gin.Context) {
|
| | if !common.LinuxDOOAuthEnabled {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "管理员未开启通过 Linux DO 登录以及注册",
|
| | })
|
| | return
|
| | }
|
| |
|
| | code := c.Query("code")
|
| | linuxdoUser, err := getLinuxdoUserInfoByCode(code, c)
|
| | if err != nil {
|
| | common.ApiError(c, err)
|
| | return
|
| | }
|
| |
|
| | user := model.User{
|
| | LinuxDOId: strconv.Itoa(linuxdoUser.Id),
|
| | }
|
| |
|
| | if model.IsLinuxDOIdAlreadyTaken(user.LinuxDOId) {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "该 Linux DO 账户已被绑定",
|
| | })
|
| | return
|
| | }
|
| |
|
| | session := sessions.Default(c)
|
| | id := session.Get("id")
|
| | user.Id = id.(int)
|
| |
|
| | err = user.FillUserById()
|
| | if err != nil {
|
| | common.ApiError(c, err)
|
| | return
|
| | }
|
| |
|
| | user.LinuxDOId = strconv.Itoa(linuxdoUser.Id)
|
| | err = user.Update(false)
|
| | if err != nil {
|
| | common.ApiError(c, err)
|
| | return
|
| | }
|
| |
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": true,
|
| | "message": "bind",
|
| | })
|
| | }
|
| |
|
| | func getLinuxdoUserInfoByCode(code string, c *gin.Context) (*LinuxdoUser, error) {
|
| | if code == "" {
|
| | return nil, errors.New("invalid code")
|
| | }
|
| |
|
| |
|
| | tokenEndpoint := common.GetEnvOrDefaultString("LINUX_DO_TOKEN_ENDPOINT", "https://connect.linux.do/oauth2/token")
|
| | credentials := common.LinuxDOClientId + ":" + common.LinuxDOClientSecret
|
| | basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(credentials))
|
| |
|
| |
|
| | scheme := "http"
|
| | if c.Request.TLS != nil {
|
| | scheme = "https"
|
| | }
|
| | redirectURI := fmt.Sprintf("%s://%s/api/oauth/linuxdo", scheme, c.Request.Host)
|
| |
|
| | data := url.Values{}
|
| | data.Set("grant_type", "authorization_code")
|
| | data.Set("code", code)
|
| | data.Set("redirect_uri", redirectURI)
|
| |
|
| | req, err := http.NewRequest("POST", tokenEndpoint, strings.NewReader(data.Encode()))
|
| | if err != nil {
|
| | return nil, err
|
| | }
|
| |
|
| | req.Header.Set("Authorization", basicAuth)
|
| | req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
| | req.Header.Set("Accept", "application/json")
|
| |
|
| | client := http.Client{Timeout: 5 * time.Second}
|
| | res, err := client.Do(req)
|
| | if err != nil {
|
| | return nil, errors.New("failed to connect to Linux DO server")
|
| | }
|
| | defer res.Body.Close()
|
| |
|
| | var tokenRes struct {
|
| | AccessToken string `json:"access_token"`
|
| | Message string `json:"message"`
|
| | }
|
| | if err := json.NewDecoder(res.Body).Decode(&tokenRes); err != nil {
|
| | return nil, err
|
| | }
|
| |
|
| | if tokenRes.AccessToken == "" {
|
| | return nil, fmt.Errorf("failed to get access token: %s", tokenRes.Message)
|
| | }
|
| |
|
| |
|
| | userEndpoint := common.GetEnvOrDefaultString("LINUX_DO_USER_ENDPOINT", "https://connect.linux.do/api/user")
|
| | req, err = http.NewRequest("GET", userEndpoint, nil)
|
| | if err != nil {
|
| | return nil, err
|
| | }
|
| | req.Header.Set("Authorization", "Bearer "+tokenRes.AccessToken)
|
| | req.Header.Set("Accept", "application/json")
|
| |
|
| | res2, err := client.Do(req)
|
| | if err != nil {
|
| | return nil, errors.New("failed to get user info from Linux DO")
|
| | }
|
| | defer res2.Body.Close()
|
| |
|
| | var linuxdoUser LinuxdoUser
|
| | if err := json.NewDecoder(res2.Body).Decode(&linuxdoUser); err != nil {
|
| | return nil, err
|
| | }
|
| |
|
| | if linuxdoUser.Id == 0 {
|
| | return nil, errors.New("invalid user info returned")
|
| | }
|
| |
|
| | return &linuxdoUser, nil
|
| | }
|
| |
|
| | func LinuxdoOAuth(c *gin.Context) {
|
| | session := sessions.Default(c)
|
| |
|
| | errorCode := c.Query("error")
|
| | if errorCode != "" {
|
| | errorDescription := c.Query("error_description")
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": errorDescription,
|
| | })
|
| | return
|
| | }
|
| |
|
| | state := c.Query("state")
|
| | if state == "" || session.Get("oauth_state") == nil || state != session.Get("oauth_state").(string) {
|
| | c.JSON(http.StatusForbidden, gin.H{
|
| | "success": false,
|
| | "message": "state is empty or not same",
|
| | })
|
| | return
|
| | }
|
| |
|
| | username := session.Get("username")
|
| | if username != nil {
|
| | LinuxDoBind(c)
|
| | return
|
| | }
|
| |
|
| | if !common.LinuxDOOAuthEnabled {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "管理员未开启通过 Linux DO 登录以及注册",
|
| | })
|
| | return
|
| | }
|
| |
|
| | code := c.Query("code")
|
| | linuxdoUser, err := getLinuxdoUserInfoByCode(code, c)
|
| | if err != nil {
|
| | common.ApiError(c, err)
|
| | return
|
| | }
|
| |
|
| | user := model.User{
|
| | LinuxDOId: strconv.Itoa(linuxdoUser.Id),
|
| | }
|
| |
|
| |
|
| | if model.IsLinuxDOIdAlreadyTaken(user.LinuxDOId) {
|
| | err := user.FillUserByLinuxDOId()
|
| | if err != nil {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": err.Error(),
|
| | })
|
| | return
|
| | }
|
| | if user.Id == 0 {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "用户已注销",
|
| | })
|
| | return
|
| | }
|
| | } else {
|
| | if common.RegisterEnabled {
|
| | if linuxdoUser.TrustLevel >= common.LinuxDOMinimumTrustLevel {
|
| | user.Username = "linuxdo_" + strconv.Itoa(model.GetMaxUserId()+1)
|
| | user.DisplayName = linuxdoUser.Name
|
| | user.Role = common.RoleCommonUser
|
| | user.Status = common.UserStatusEnabled
|
| |
|
| | affCode := session.Get("aff")
|
| | inviterId := 0
|
| | if affCode != nil {
|
| | inviterId, _ = model.GetUserIdByAffCode(affCode.(string))
|
| | }
|
| |
|
| | if err := user.Insert(inviterId); err != nil {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": err.Error(),
|
| | })
|
| | return
|
| | }
|
| | } else {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "Linux DO 信任等级未达到管理员设置的最低信任等级",
|
| | })
|
| | return
|
| | }
|
| | } else {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "success": false,
|
| | "message": "管理员关闭了新用户注册",
|
| | })
|
| | return
|
| | }
|
| | }
|
| |
|
| | if user.Status != common.UserStatusEnabled {
|
| | c.JSON(http.StatusOK, gin.H{
|
| | "message": "用户已被封禁",
|
| | "success": false,
|
| | })
|
| | return
|
| | }
|
| |
|
| | setupLogin(&user, c)
|
| | }
|
| |
|