Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.23.4
toolchain go1.23.6

require (
github.com/eolinker/ap-account v1.0.15
github.com/eolinker/ap-account v1.0.16
github.com/eolinker/eosc v0.18.3
github.com/eolinker/go-common v1.1.7
github.com/gabriel-vasile/mimetype v1.4.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eolinker/ap-account v1.0.15 h1:n6DJeL6RHZ8eLlZUcY2U3H4d/GPaA5oelAx3R0E6yL8=
github.com/eolinker/ap-account v1.0.15/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g=
github.com/eolinker/ap-account v1.0.16 h1:v1VvSeQ2AvxAvkYT4n4APqZdWS8d1CbA/1O0LYEyNM4=
github.com/eolinker/ap-account v1.0.16/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g=
github.com/eolinker/eosc v0.18.3 h1:3IK5HkAPnJRfLbQ0FR7kWsZr6Y/OiqqGazvN1q2BL5A=
github.com/eolinker/eosc v0.18.3/go.mod h1:O9PQQXFCpB6fjHf+oFt/LN6EOAv779ItbMixMKCfTfk=
github.com/eolinker/go-common v1.1.7 h1:bi7wDmlCYQGjS3k8Bz/o+Mo9aMJAzmPsBLXWurxPfwk=
Expand Down
1 change: 1 addition & 0 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package main
import (
_ "github.com/APIParkLab/APIPark/frontend"
_ "github.com/APIParkLab/APIPark/gateway/apinto"
_ "github.com/APIParkLab/APIPark/login_driver/feishu"
_ "github.com/APIParkLab/APIPark/plugins/core"
_ "github.com/APIParkLab/APIPark/plugins/openapi"
_ "github.com/APIParkLab/APIPark/plugins/permit"
Expand Down
56 changes: 56 additions & 0 deletions login_driver/feishu/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package feishu

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)

var (
client = http.Client{
Timeout: 10 * time.Second,
}
)

func SendRequest[T any](uri string, method string, header http.Header, query url.Values, body []byte) (*T, error) {
if uri == "" {
return nil, fmt.Errorf("invalid URL")
}

req, err := http.NewRequest(method, uri, bytes.NewReader(body))
if err != nil {
return nil, err
}

if query != nil {
req.URL.RawQuery = query.Encode()
}

if header != nil {
req.Header = header
}

resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
result := new(T)
err = json.Unmarshal(respBody, result)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("status code error: %d, response: %s", resp.StatusCode, respBody)
}

return result, nil
}
27 changes: 27 additions & 0 deletions login_driver/feishu/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package feishu

type UserTokenResponse struct {
Code int `json:"code"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
RefreshTokenExpiresIn int `json:"refresh_token_expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}

type UserInfoResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data UserInfo `json:"data"`
}

type UserInfo struct {
Name string `json:"name"`
OpenID string `json:"open_id"`
UnionId string `json:"union_id"`
Email string `json:"email"`
Mobile string `json:"mobile"`
}
144 changes: 144 additions & 0 deletions login_driver/feishu/feishu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package feishu

import (
"context"
"errors"
"fmt"
"net/http"
"net/url"

"github.com/eolinker/eosc/common/bean"

"github.com/eolinker/ap-account/service/user"

"github.com/eolinker/go-common/utils"

"github.com/google/uuid"

"gorm.io/gorm"

"github.com/eolinker/ap-account/service/account"

"github.com/eolinker/ap-account/auth_driver"
)

const (
name = "feishu"
title = "飞书"
getTokenUri = "https://open.feishu.cn/open-apis/authen/v2/oauth/token"
getUserInfoUri = "https://open.feishu.cn/open-apis/authen/v1/user_info"
)

var _ auth_driver.IDriver = (*Driver)(nil)

func init() {
d := &Driver{}
bean.Autowired(&d.accountService)
bean.Autowired(&d.userService)
auth_driver.Register(name, d)
}

type Driver struct {
accountService account.IAccountService `autowired:""`
userService user.IUserService `autowired:""`
}

func (d *Driver) FilterConfig(config map[string]string) {
delete(config, "client_secret")
}

func (d *Driver) Name() string {
return name
}

func (d *Driver) Title() string {
return title
}

func (d *Driver) ThirdLogin(ctx context.Context, args map[string]string) (string, error) {
code, ok := args["code"]
if !ok {
return "", fmt.Errorf("missing code parameter")
}
clientId, ok := args["client_id"]
if !ok {
return "", fmt.Errorf("missing client_id parameter")
}
clientSecret, ok := args["client_secret"]
if !ok {
return "", fmt.Errorf("missing client_secret parameter")
}
tokenResp, err := getUserToken(code, clientId, clientSecret)
if err != nil {
return "", err
}
userInfoResp, err := getUserInfo(tokenResp.TokenType, tokenResp.AccessToken)
if err != nil {
return "", err
}
userId := userInfoResp.Data.UnionId
username := userInfoResp.Data.Name
email := userInfoResp.Data.Email
mobile := userInfoResp.Data.Mobile
info, err := d.accountService.GetIdentifier(ctx, name, userId)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return "", err
}
uId := uuid.NewString()
err = d.accountService.Save(ctx, name, uId, userId, utils.Md5(fmt.Sprintf("%s%s", uId, userId)))
if err != nil {
return "", err
}
_, err = d.userService.Create(ctx, uId, username, email, mobile, "")
if err != nil {
return "", err
}
return userId, nil
}
_, err = d.userService.Update(ctx, info.Uid, &username, &email, &mobile)
if err != nil {
return "", err
}

return userId, nil
}

func getUserToken(code string, clientId string, clientSecret string) (*UserTokenResponse, error) {
headers := http.Header{}
headers.Set("Content-Type", "application/json")
body := url.Values{}
body.Set("grant_type", "authorization_code")
body.Set("code", code)
body.Set("client_id", clientId)
body.Set("client_secret", clientSecret)
resp, err := SendRequest[UserTokenResponse](getTokenUri, http.MethodPost, headers, nil, []byte(body.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to get user token: %w", err)
}
if resp.Code != 0 {
return nil, fmt.Errorf("failed to get user token: %s", resp.ErrorDescription)
}
return resp, nil
}

func getUserInfo(tokenType string, token string) (*UserInfoResponse, error) {
headers := http.Header{}
headers.Set("Content-Type", "application/json")
switch tokenType {
case "Bearer":
headers.Set("Authorization", fmt.Sprintf("Bearer %s", token))
}
resp, err := SendRequest[UserInfoResponse](getUserInfoUri, http.MethodGet, headers, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get user info: %w", err)
}
if resp.Code != 0 {
return nil, fmt.Errorf("failed to get user info: %s", resp.Msg)
}
return resp, nil
}

func (d *Driver) Delete(ctx context.Context, ids ...string) error {
return d.accountService.OnRemoveUsers(ctx, ids...)
}
10 changes: 10 additions & 0 deletions resources/access/access.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ system:
value: 'manager'
dependents:
- system.settings.ai_api.view
- name: login
value: 'login'
children:
- name: view
value: 'view'
guest_allow: true
- name: manager
value: 'manager'
dependents:
- system.settings.login.view
- name: ai log
value: 'ai_log'
children:
Expand Down
4 changes: 4 additions & 0 deletions resources/access/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ system:
- system.settings.general.view
- system.settings.log_configuration.manager
- system.settings.log_configuration.view
- system.settings.login.manager
- system.settings.login.view
- system.settings.mcp.view
- system.settings.mcp.manager
- system.settings.role.view
Expand Down Expand Up @@ -69,6 +71,8 @@ system:
- system.settings.general.view
- system.settings.log_configuration.manager
- system.settings.log_configuration.view
- system.settings.login.manager
- system.settings.login.view
- system.settings.ssl_certificate.manager
- system.settings.ssl_certificate.view
- system.settings.strategy.view
Expand Down