From 7a2230f63eeb9f62d6e36773ba72f4ede5070c06 Mon Sep 17 00:00:00 2001 From: Kininaru Date: Fri, 30 Jul 2021 14:15:10 +0800 Subject: [PATCH] feat: expose email and sms APIs as services to SDK (#202) Signed-off-by: Kininaru invalid receivers --- controllers/service.go | 159 ++++++++++++++++++++++++++++++++++++++++ controllers/util.go | 12 ++- object/application.go | 13 ++++ routers/authz_filter.go | 5 +- routers/router.go | 3 + 5 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 controllers/service.go diff --git a/controllers/service.go b/controllers/service.go new file mode 100644 index 00000000..daac43da --- /dev/null +++ b/controllers/service.go @@ -0,0 +1,159 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed 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. + +// Casdoor will expose its providers as services to SDK +// We are going to implement those services as APIs here + +package controllers + +import ( + "encoding/json" + + "github.com/casbin/casdoor/object" + "github.com/casbin/casdoor/util" + sender "github.com/casdoor/go-sms-sender" +) + +// @Title SendEmail +// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs. +// @Param clientId query string true "The clientId of the application" +// @Param clientSecret query string true "The clientSecret of the application" +// @Param body body emailForm true "Details of the email request" +// @Success 200 {object} Response object +// @router /api/send-email [post] +func (c *ApiController) SendEmail() { + clientId := c.Input().Get("clientId") + clientSecret := c.Input().Get("clientSecret") + app := object.GetApplicationByClientIdAndSecret(clientId, clientSecret) + if app == nil { + c.ResponseError("Invalid clientId or clientSecret.") + return + } + + provider := app.GetEmailProvider() + if provider == nil { + c.ResponseError("No Email provider for this application.") + return + } + + var emailForm struct { + Title string `json:"title"` + Content string `json:"content"` + Receivers []string `json:"receivers"` + Sender string `json:"sender"` + } + + err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm) + if err != nil { + c.ResponseError("Request body error.") + return + } + + if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) { + c.ResponseError("Missing parameters.") + return + } + + var invalidEmails []string + for _, receiver := range emailForm.Receivers { + if !util.IsEmailValid(receiver) { + invalidEmails = append(invalidEmails, receiver) + } + } + + if len(invalidEmails) != 0 { + c.ResponseError("Invalid Email addresses", invalidEmails) + return + } + + ok := 0 + for _, receiver := range emailForm.Receivers { + if msg := object.SendEmail( + provider, + emailForm.Title, + emailForm.Content, + receiver, + emailForm.Sender); + len(msg) == 0 { + ok++ + } + } + + c.Data["json"] = Response{Status: "ok", Data: ok} + c.ServeJSON() +} + +// @Title SendSms +// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs. +// @Param clientId query string true "The clientId of the application" +// @Param clientSecret query string true "The clientSecret of the application" +// @Param body body smsForm true "Details of the sms request" +// @Success 200 {object} Response object +// @router /api/send-sms [post] +func (c *ApiController) SendSms() { + clientId := c.Input().Get("clientId") + clientSecret := c.Input().Get("clientSecret") + app := object.GetApplicationByClientIdAndSecret(clientId, clientSecret) + if app == nil { + c.ResponseError("Invalid clientId or clientSecret.") + return + } + + provider := app.GetSmsProvider() + if provider == nil { + c.ResponseError("No SMS provider for this application.") + return + } + + client := sender.NewSmsClient( + provider.Type, + provider.ClientId, + provider.ClientSecret, + provider.SignName, + provider.RegionId, + provider.TemplateCode, + provider.AppId, + ) + if client == nil { + c.ResponseError("Invalid provider info.") + return + } + + var smsForm struct { + Receivers []string `json:"receivers"` + Parameters map[string]string `json:"parameters"` + } + + err := json.Unmarshal(c.Ctx.Input.RequestBody, &smsForm) + if err != nil { + c.ResponseError("Request body error.") + return + } + + var invalidReceivers []string + for _, receiver := range smsForm.Receivers { + if !util.IsPhoneCnValid(receiver) { + invalidReceivers = append(invalidReceivers, receiver) + } + } + + if len(invalidReceivers) != 0{ + c.ResponseError("Invalid phone numbers", invalidReceivers) + return + } + + client.SendMessage(smsForm.Parameters, smsForm.Receivers...) + c.Data["json"] = Response{Status: "ok"} + c.ServeJSON() +} diff --git a/controllers/util.go b/controllers/util.go index ffbedb5b..48cec1f2 100644 --- a/controllers/util.go +++ b/controllers/util.go @@ -49,8 +49,16 @@ func InitHttpClient() { //println("Response status: %s", resp.Status) } -func (c *ApiController) ResponseError(error string) { - c.Data["json"] = Response{Status: "error", Msg: error} +func (c *ApiController) ResponseError(error string, data ...interface{}) { + resp := Response{Status: "error", Msg: error} + switch len(data) { + case 2: + resp.Data2 = data[1] + fallthrough + case 1: + resp.Data = data[0] + } + c.Data["json"] = resp c.ServeJSON() } diff --git a/object/application.go b/object/application.go index 049be193..8021b48f 100644 --- a/object/application.go +++ b/object/application.go @@ -142,6 +142,19 @@ func GetApplicationByClientId(clientId string) *Application { } } +func GetApplicationByClientIdAndSecret(clientId, clientSecret string) *Application { + if util.IsStrsEmpty(clientId, clientSecret) { + return nil + } + + app := GetApplicationByClientId(clientId) + if app == nil || app.ClientSecret != clientSecret { + return nil + } + + return app +} + func GetApplication(id string) *Application { owner, name := util.GetOwnerAndNameFromId(id) return getApplication(owner, name) diff --git a/routers/authz_filter.go b/routers/authz_filter.go index 86ab65dc..73ebfd63 100644 --- a/routers/authz_filter.go +++ b/routers/authz_filter.go @@ -33,9 +33,8 @@ type Object struct { } func getUsernameByClientIdSecret(ctx *context.Context) string { - requestUri := ctx.Request.RequestURI - clientId := parseQuery(requestUri, "clientId") - clientSecret := parseQuery(requestUri, "clientSecret") + clientId := ctx.Input.Query("clientId") + clientSecret := ctx.Input.Query("clientSecret") if len(clientId) == 0 || len(clientSecret) == 0 { return "" } diff --git a/routers/router.go b/routers/router.go index 6030ee53..2654ce1f 100644 --- a/routers/router.go +++ b/routers/router.go @@ -95,5 +95,8 @@ func initAPI() { beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords") beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter") + + beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail") + beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms") }