From 08a8688aa727b3c8f1a434a60889d66e1c40bfe0 Mon Sep 17 00:00:00 2001 From: JustSong Date: Sun, 10 Nov 2024 10:46:28 +0800 Subject: [PATCH] feat: support channel level auth --- README.md | 1 + controller/channel.go | 2 ++ controller/message.go | 51 +++++++++++++++++++------------ model/channel.go | 27 ++++++++-------- web/src/components/PushSetting.js | 24 ++++++++++++--- 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 6319890..de16689 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ proxy_send_timeout 300s; 15. `tencent_alarm`:通过腾讯云监控告警进行推送,仅支持 `description` 字段。 16. `none`:仅保存到数据库,不做推送。 5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。 + * 注意令牌有两种,一种是全局鉴权令牌,一种是通道维度的令牌,前者可以鉴权任何通道,后者只能鉴权指定通道。 6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。 7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。 1. `@all`:推送给所有用户。 diff --git a/controller/channel.go b/controller/channel.go index c4af5d7..88d6152 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -135,6 +135,7 @@ func AddChannel(c *gin.Context) { URL: channel_.URL, Other: channel_.Other, CreatedTime: common.GetTimestamp(), + Token: channel_.Token, } err = cleanChannel.Insert() if err != nil { @@ -206,6 +207,7 @@ func UpdateChannel(c *gin.Context) { cleanChannel.AccountId = channel_.AccountId cleanChannel.URL = channel_.URL cleanChannel.Other = channel_.Other + cleanChannel.Token = channel_.Token } err = cleanChannel.Update() if err != nil { diff --git a/controller/message.go b/controller/message.go index 4377f6a..d762a18 100644 --- a/controller/message.go +++ b/controller/message.go @@ -10,6 +10,7 @@ import ( "message-pusher/model" "net/http" "strconv" + "strings" "time" ) @@ -46,7 +47,7 @@ func GetPushMessage(c *gin.Context) { func PostPushMessage(c *gin.Context) { var message model.Message - if c.Request.Header.Get("Content-Type") == "application/json" { + if strings.Contains(strings.ToLower(c.Request.Header.Get("Content-Type")), "application/json") { // Looks like the user is using JSON message = model.Message{} err := json.NewDecoder(c.Request.Body).Decode(&message) @@ -110,28 +111,26 @@ func pushMessageHelper(c *gin.Context, message *model.Message) { }) return } - if user.Token != "" && user.Token != " " { - if message.Token == "" { - message.Token = c.Request.Header.Get("Authorization") - if message.Token == "" { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "token 为空", - }) - return - } - } - if user.Token != message.Token { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无效的 token", - }) - return - } + if message.Token == "" { + message.Token = strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer ") } processMessage(c, message, &user) } +func authMessage(messageToken string, userToken string, channelToken *string) bool { + if userToken != "" { + if messageToken == userToken { + return true + } + } + if channelToken != nil && *channelToken != "" { + if messageToken != *channelToken { + return false + } + } + return true +} + func processMessage(c *gin.Context, message *model.Message, user *model.User) { if message.Title == "" { message.Title = common.SystemName @@ -150,6 +149,20 @@ func processMessage(c *gin.Context, message *model.Message, user *model.User) { }) return } + if !authMessage(message.Token, user.Token, channel_.Token) { + if message.Token == "" { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "通道维度或用户维度设置了鉴权令牌,需要提供鉴权令牌", + }) + return + } + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "无效的 token", + }) + return + } err = saveAndSendMessage(user, message, channel_) if err != nil { c.JSON(http.StatusOK, gin.H{ diff --git a/model/channel.go b/model/channel.go index a060d3e..014665a 100644 --- a/model/channel.go +++ b/model/channel.go @@ -25,18 +25,19 @@ const ( ) type Channel struct { - Id int `json:"id"` - Type string `json:"type" gorm:"type:varchar(32)"` - UserId int `json:"user_id" gorm:"uniqueIndex:name_user_id;index"` - Name string `json:"name" gorm:"type:varchar(32);uniqueIndex:name_user_id"` - Description string `json:"description"` - Status int `json:"status" gorm:"default:1"` // enabled, disabled - Secret string `json:"secret" gorm:"index"` - AppId string `json:"app_id"` - AccountId string `json:"account_id"` - URL string `json:"url" gorm:"column:url"` - Other string `json:"other"` - CreatedTime int64 `json:"created_time" gorm:"bigint"` + Id int `json:"id"` + Type string `json:"type" gorm:"type:varchar(32)"` + UserId int `json:"user_id" gorm:"uniqueIndex:name_user_id;index"` + Name string `json:"name" gorm:"type:varchar(32);uniqueIndex:name_user_id"` + Description string `json:"description"` + Status int `json:"status" gorm:"default:1"` // enabled, disabled + Secret string `json:"secret" gorm:"index"` + AppId string `json:"app_id"` + AccountId string `json:"account_id"` + URL string `json:"url" gorm:"column:url"` + Other string `json:"other"` + CreatedTime int64 `json:"created_time" gorm:"bigint"` + Token *string `json:"token" gorm:"token"` } type BriefChannel struct { @@ -120,7 +121,7 @@ func (channel *Channel) UpdateStatus(status int) error { // Update Make sure your token's fields is completed, because this will update non-zero values func (channel *Channel) Update() error { var err error - err = DB.Model(channel).Select("type", "name", "description", "secret", "app_id", "account_id", "url", "other", "status").Updates(channel).Error + err = DB.Model(channel).Select("type", "name", "description", "secret", "app_id", "account_id", "url", "other", "status", "token").Updates(channel).Error return err } diff --git a/web/src/components/PushSetting.js b/web/src/components/PushSetting.js index 81bc182..c008db9 100644 --- a/web/src/components/PushSetting.js +++ b/web/src/components/PushSetting.js @@ -1,6 +1,12 @@ import React, { useEffect, useState } from 'react'; import { Button, Form, Grid, Header, Message } from 'semantic-ui-react'; -import { API, showError, showSuccess, testChannel } from '../helpers'; +import { + API, + generateToken, + showError, + showSuccess, + testChannel, +} from '../helpers'; import { loadUser, loadUserChannels } from '../helpers/loader'; const PushSetting = () => { @@ -70,15 +76,25 @@ const PushSetting = () => { options={channels} value={user.channel} onChange={handleInputChange} - width={6} + width={5} /> { + console.log('generate token'); + setUser((inputs) => ({ + ...inputs, + token: generateToken(16), + })); + }, + }} />