gitee.com/larksuite/oapi-sdk-go/v3@v3.0.3/card/card.go (about) 1 /* 2 * MIT License 3 * 4 * Copyright (c) 2022 Lark Technologies Pte. Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 * 8 * The above copyright notice and this permission notice, shall be included in all copies or substantial portions of the Software. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 */ 12 13 package larkcard 14 15 import ( 16 "context" 17 "crypto/sha1" 18 "encoding/json" 19 "errors" 20 "fmt" 21 "net/http" 22 "strings" 23 24 larkcore "gitee.com/larksuite/oapi-sdk-go/v3/core" 25 larkevent "gitee.com/larksuite/oapi-sdk-go/v3/event" 26 ) 27 28 type CardActionHandler struct { 29 verificationToken string 30 eventEncryptKey string 31 handler func(context.Context, *CardAction) (interface{}, error) 32 *larkcore.Config 33 } 34 35 func processError(ctx context.Context, logger larkcore.Logger, path string, err error) *larkevent.EventResp { 36 header := map[string][]string{} 37 statusCode := http.StatusInternalServerError 38 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 39 eventResp := &larkevent.EventResp{ 40 Header: header, 41 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, err.Error())), 42 StatusCode: statusCode, 43 } 44 logger.Error(ctx, fmt.Sprintf("handle cardAcion,path:%s, err: %v", path, err)) 45 return eventResp 46 } 47 48 func recoveryResult() *larkevent.EventResp { 49 header := map[string][]string{} 50 statusCode := http.StatusInternalServerError 51 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 52 eventResp := &larkevent.EventResp{ 53 Header: header, 54 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, "Server Internal Error")), 55 StatusCode: statusCode, 56 } 57 return eventResp 58 } 59 60 func (h *CardActionHandler) Handle(ctx context.Context, req *larkevent.EventReq) (eventResp *larkevent.EventResp) { 61 h.Config.Logger.Debug(ctx, fmt.Sprintf("card request: header:%v,body:%s", req.Header, string(req.Body))) 62 defer func() { 63 if err := recover(); err != nil { 64 h.Config.Logger.Error(ctx, fmt.Sprintf("handle cardAction,path:%s, error:%v", req.RequestURI, err)) 65 eventResp = recoveryResult() 66 } 67 }() 68 cardAction := &CardAction{} 69 err := json.Unmarshal(req.Body, cardAction) 70 if err != nil { 71 return processError(ctx, h.Config.Logger, req.RequestURI, err) 72 } 73 cardAction.EventReq = req 74 75 if larkevent.ReqType(cardAction.Type) != larkevent.ReqTypeChallenge { 76 err = h.VerifySign(ctx, req) 77 if err != nil { 78 return processError(ctx, h.Config.Logger, req.RequestURI, err) 79 } 80 } 81 82 result, err := h.DoHandle(ctx, cardAction) 83 if err != nil { 84 return processError(ctx, h.Config.Logger, req.RequestURI, err) 85 } 86 return result 87 } 88 89 func (h *CardActionHandler) Logger() larkcore.Logger { 90 return h.Config.Logger 91 } 92 93 func (h *CardActionHandler) InitConfig(options ...larkevent.OptionFunc) { 94 for _, option := range options { 95 option(h.Config) 96 } 97 larkcore.NewLogger(h.Config) 98 } 99 100 func NewCardActionHandler(verificationToken, eventEncryptKey string, handler func(context.Context, *CardAction) (interface{}, error)) *CardActionHandler { 101 h := &CardActionHandler{ 102 verificationToken: verificationToken, 103 eventEncryptKey: eventEncryptKey, 104 handler: handler, 105 Config: &larkcore.Config{Logger: larkcore.NewEventLogger()}, 106 } 107 return h 108 } 109 110 func (h *CardActionHandler) Event() interface{} { 111 return &CardAction{} 112 } 113 114 var notFoundCardHandlerErr = errors.New("card action handler not found") 115 116 func (h *CardActionHandler) AuthByChallenge(ctx context.Context, cardAction *CardAction) (*larkevent.EventResp, error) { 117 header := map[string][]string{} 118 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 119 hookType := larkevent.ReqType(cardAction.Type) 120 challenge := cardAction.Challenge 121 if hookType == larkevent.ReqTypeChallenge { 122 if h.verificationToken != cardAction.Token { 123 err := errors.New("the result of auth by challenge failed") 124 return nil, err 125 } 126 eventResp := larkevent.EventResp{ 127 Header: header, 128 Body: []byte(fmt.Sprintf(larkevent.ChallengeResponseFormat, challenge)), 129 StatusCode: http.StatusOK, 130 } 131 h.Config.Logger.Info(ctx, fmt.Sprintf("AuthByChallenge Success")) 132 return &eventResp, nil 133 } 134 return nil, nil 135 } 136 func (h *CardActionHandler) DoHandle(ctx context.Context, cardAction *CardAction) (*larkevent.EventResp, error) { 137 var err error 138 // auth by challenge 139 resp, err := h.AuthByChallenge(ctx, cardAction) 140 if err != nil { 141 return nil, err 142 } 143 if resp != nil { 144 return resp, nil 145 } 146 147 // 校验行为执行器 148 handler := h.handler 149 if handler == nil { 150 err = notFoundCardHandlerErr 151 return nil, err 152 } 153 154 // 执行事件行为处理器 155 result, err := handler(ctx, cardAction) 156 if err != nil { 157 return nil, err 158 } 159 160 header := map[string][]string{} 161 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 162 if result == nil { 163 eventResp := &larkevent.EventResp{ 164 Header: header, 165 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, "success")), 166 StatusCode: http.StatusOK, 167 } 168 return eventResp, nil 169 } 170 171 var respBody []byte 172 switch r := result.(type) { 173 case string: 174 respBody = []byte(r) 175 case *CustomResp: 176 statusCode := r.StatusCode 177 if statusCode == 0 { 178 statusCode = http.StatusOK 179 } 180 181 b, err := json.Marshal(r.Body) 182 if err != nil { 183 return nil, err 184 } 185 186 eventResp := &larkevent.EventResp{ 187 Header: header, 188 Body: b, 189 StatusCode: statusCode, 190 } 191 return eventResp, nil 192 default: 193 respBody, err = json.Marshal(result) 194 } 195 196 eventResp := &larkevent.EventResp{ 197 Header: header, 198 Body: respBody, 199 StatusCode: http.StatusOK, 200 } 201 202 return eventResp, err 203 } 204 205 func (h *CardActionHandler) VerifySign(ctx context.Context, req *larkevent.EventReq) error { 206 if h.verificationToken == "" { 207 return nil 208 } 209 210 // 解析签名头 211 requestTimestamps := req.Header[larkevent.EventRequestTimestamp] 212 requestNonces := req.Header[larkevent.EventRequestNonce] 213 214 var requestTimestamp = "" 215 var requestNonce = "" 216 if len(requestTimestamps) > 0 { 217 requestTimestamp = requestTimestamps[0] 218 } 219 if len(requestNonces) > 0 { 220 requestNonce = requestNonces[0] 221 } 222 223 // 执行sha1签名计算 224 targetSign := Signature(requestTimestamp, requestNonce, 225 h.verificationToken, 226 string(req.Body)) 227 228 sourceSigns := req.Header[larkevent.EventSignature] 229 var sourceSign = "" 230 if len(sourceSigns) > 0 { 231 sourceSign = sourceSigns[0] 232 } 233 234 // 验签 235 if targetSign == sourceSign { 236 return nil 237 } 238 return errors.New("the result of signature verification failed") 239 } 240 241 func Signature(timestamp, nonce, token, body string) string { 242 var b strings.Builder 243 b.WriteString(timestamp) 244 b.WriteString(nonce) 245 b.WriteString(token) 246 b.WriteString(body) 247 bs := []byte(b.String()) 248 h := sha1.New() 249 _, _ = h.Write(bs) 250 bs = h.Sum(nil) 251 return fmt.Sprintf("%x", bs) 252 }