user-center-slack/slack_user_center.go (200 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package slack_user_center
import (
"embed"
"fmt"
"net/http"
"sync"
"time"
"github.com/apache/answer-plugins/user-center-slack/i18n"
"github.com/apache/answer-plugins/util"
"github.com/apache/answer/plugin"
"github.com/gin-gonic/gin"
"github.com/patrickmn/go-cache"
"github.com/segmentfault/pacman/log"
)
//go:embed info.yaml
var Info embed.FS
type Importer struct{}
type UserCenter struct {
Config *UserCenterConfig
SlackClient *SlackClient
UserConfigCache *UserConfigCache
Cache *cache.Cache
syncLock sync.Mutex
syncing bool
syncSuccess bool
syncTime time.Time
importerFunc plugin.ImporterFunc
}
func (uc *UserCenter) RegisterUnAuthRouter(r *gin.RouterGroup) {
r.GET("/slack/login/url", uc.GetSlackRedirectURL)
r.POST("/slack/slash", uc.SlashCommand)
}
func (uc *UserCenter) RegisterAuthUserRouter(r *gin.RouterGroup) {
}
func (uc *UserCenter) RegisterAuthAdminRouter(r *gin.RouterGroup) {
r.GET("/slack/sync", uc.Sync)
}
func (uc *UserCenter) AfterLogin(externalID, accessToken string) {
log.Debugf("user %s is login", externalID)
uc.Cache.Set(externalID, accessToken, time.Minute*5)
}
func (uc *UserCenter) UserStatus(externalID string) (userStatus plugin.UserStatus) {
if len(externalID) == 0 {
return plugin.UserStatusAvailable
}
var err error
userDetailInfo := uc.SlackClient.UserInfoMapping[externalID]
if userDetailInfo == nil {
userDetailInfo, err = uc.SlackClient.GetUserDetailInfo(externalID)
if err != nil {
log.Errorf("get user detail info failed: %v", err)
}
}
if userDetailInfo == nil {
return plugin.UserStatusDeleted
}
switch userDetailInfo.Status {
case 1:
return plugin.UserStatusAvailable
case 2:
return plugin.UserStatusSuspended
default:
return plugin.UserStatusDeleted
}
}
func init() {
uc := &UserCenter{
Config: &UserCenterConfig{},
UserConfigCache: NewUserConfigCache(),
SlackClient: NewSlackClient("", ""),
Cache: cache.New(5*time.Minute, 10*time.Minute),
syncLock: sync.Mutex{},
}
plugin.Register(uc)
uc.CronSyncData()
}
func (uc *UserCenter) Info() plugin.Info {
info := &util.Info{}
info.GetInfo(Info)
return plugin.Info{
Name: plugin.MakeTranslator(i18n.InfoName),
SlugName: info.SlugName,
Description: plugin.MakeTranslator(i18n.InfoDescription),
Author: info.Author,
Version: info.Version,
Link: info.Link,
}
}
func (uc *UserCenter) Description() plugin.UserCenterDesc {
redirectURL := uc.BuildSlackBaseRedirectURL()
desc := plugin.UserCenterDesc{
Name: "Slack",
DisplayName: plugin.MakeTranslator(i18n.InfoName),
Icon: "",
Url: "",
LoginRedirectURL: redirectURL,
SignUpRedirectURL: redirectURL,
RankAgentEnabled: false,
UserStatusAgentEnabled: false,
UserRoleAgentEnabled: false,
MustAuthEmailEnabled: true,
EnabledOriginalUserSystem: true,
}
return desc
}
func (uc *UserCenter) ControlCenterItems() []plugin.ControlCenter {
var controlCenterItems []plugin.ControlCenter
return controlCenterItems
}
func (uc *UserCenter) LoginCallback(ctx *plugin.GinContext) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
log.Debugf("Processing LoginCallback")
CallbackURL := ctx.Request.URL.String()
log.Debugf("callbackURL in SlackLoginCallback:", CallbackURL)
code := ctx.Query("code")
if len(code) == 0 {
return nil, fmt.Errorf("code is empty")
}
state := ctx.Query("state")
if len(state) == 0 {
return nil, fmt.Errorf("state is empty")
}
log.Debugf("request code: %s, state: %s", code, state)
expectedState, exist := uc.Cache.Get("oauth_state_" + state)
if !exist {
fmt.Println("State not found in cache or expired")
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or expired state"})
return
}
if state != expectedState {
fmt.Println("State mismatch")
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid state"})
return
}
log.Debugf("State validated successfully")
info, err := uc.SlackClient.AuthUser(code)
if err != nil {
return nil, fmt.Errorf("auth user failed: %w", err)
}
if !info.IsAvailable {
return nil, fmt.Errorf("user is not available")
}
//Get Email
if len(info.Profile.Email) == 0 {
ctx.Redirect(http.StatusFound, "/user-center/auth-failed")
return nil, fmt.Errorf("user email is empty")
}
userInfo = &plugin.UserCenterBasicUserInfo{}
userInfo.ExternalID = info.ID
userInfo.Username = info.ID
userInfo.DisplayName = info.Name
userInfo.Email = info.Profile.Email
userInfo.Rank = 0
userInfo.Mobile = ""
userInfo.Avatar = info.Profile.Image192
uc.Cache.Set(state, userInfo.ExternalID, time.Minute*5)
return userInfo, nil
}
func (uc *UserCenter) SignUpCallback(ctx *plugin.GinContext) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
return uc.LoginCallback(ctx)
}
func (uc *UserCenter) UserInfo(externalID string) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
userDetailInfo := uc.SlackClient.UserInfoMapping[externalID]
if userDetailInfo == nil {
userDetailInfo, err = uc.SlackClient.GetUserDetailInfo(externalID)
if err != nil {
log.Errorf("get user detail info failed: %v", err)
userInfo = &plugin.UserCenterBasicUserInfo{
ExternalID: externalID,
Status: plugin.UserStatusDeleted,
}
return userInfo, nil
}
}
userInfo = &plugin.UserCenterBasicUserInfo{
ExternalID: externalID,
Username: userDetailInfo.ID,
DisplayName: userDetailInfo.Name,
Bio: "",
}
switch userDetailInfo.Status {
case 1:
userInfo.Status = plugin.UserStatusAvailable
case 2:
userInfo.Status = plugin.UserStatusSuspended
default:
userInfo.Status = plugin.UserStatusDeleted
}
return userInfo, nil
}
func (uc *UserCenter) UserList(externalIDs []string) (userList []*plugin.UserCenterBasicUserInfo, err error) {
userList = make([]*plugin.UserCenterBasicUserInfo, 0)
return userList, nil
}
func (uc *UserCenter) UserSettings(externalID string) (userSettings *plugin.SettingInfo, err error) {
return &plugin.SettingInfo{
ProfileSettingRedirectURL: "",
AccountSettingRedirectURL: "",
}, nil
}
func (uc *UserCenter) PersonalBranding(externalID string) (branding []*plugin.PersonalBranding) {
return branding
}