Skip to content

Commit

Permalink
Specify recipient as a query param when sending OCM token by email (c…
Browse files Browse the repository at this point in the history
  • Loading branch information
gmgigi96 committed Jun 28, 2023
1 parent b5c961a commit a56a9aa
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 19 deletions.
7 changes: 7 additions & 0 deletions changelog/unreleased/ocm-token-email.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Specify recipient as a query param when sending OCM token by email

Before the email recipient when sending the OCM token was specified as a form parameter.
Now as a query parameter, as some clients does not allow in a GET request to set form values.
It also add the possibility to specify a template for the subject and the body for the token email.

https://github.com/cs3org/reva/pull/3687
118 changes: 118 additions & 0 deletions internal/http/services/sciencemesh/email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2018-2023 CERN
//
// 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.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package sciencemesh

import (
"bytes"
"html/template"
"io"
"os"

userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
)

type emailParams struct {
User *userpb.User
Token string
MeshDirectoryURL string
}

const defaultSubject = `ScienceMesh: {{.User.DisplayName}} wants to collaborate with you`

const defaultBody = `Hi
{{.User.DisplayName}} ({{.User.Mail}}) wants to start sharing OCM resources with you.
To accept the invite, please visit the following URL:
{{.MeshDirectoryURL}}?token={{.Token}}&providerDomain={{.User.Id.Idp}}
Alternatively, you can visit your mesh provider and use the following details:
Token: {{.Token}}
ProviderDomain: {{.User.Id.Idp}}
Best,
The ScienceMesh team`

func (h *tokenHandler) sendEmail(recipient string, obj *emailParams) error {
subj, err := h.generateEmailSubject(obj)
if err != nil {
return err
}

body, err := h.generateEmailBody(obj)
if err != nil {
return err
}

return h.smtpCredentials.SendMail(recipient, subj, body)
}

func (h *tokenHandler) generateEmailSubject(obj *emailParams) (string, error) {
var buf bytes.Buffer
err := h.tplSubj.Execute(&buf, obj)
return buf.String(), err
}

func (h *tokenHandler) generateEmailBody(obj *emailParams) (string, error) {
var buf bytes.Buffer
err := h.tplBody.Execute(&buf, obj)
return buf.String(), err
}

func (h *tokenHandler) initBodyTemplate(bodyTemplPath string) error {
var body string
if bodyTemplPath == "" {
body = defaultBody
} else {
f, err := os.Open(bodyTemplPath)
if err != nil {
return err
}
defer f.Close()

data, err := io.ReadAll(f)
if err != nil {
return err
}
body = string(data)
}

tpl, err := template.New("tpl_body").Parse(body)
if err != nil {
return err
}

h.tplBody = tpl
return nil
}

func (h *tokenHandler) initSubjectTemplate(subjTempl string) error {
var subj string
if subjTempl == "" {
subj = defaultSubject
} else {
subj = subjTempl
}

tpl, err := template.New("tpl_subj").Parse(subj)
if err != nil {
return err
}
h.tplSubj = tpl
return nil
}
3 changes: 3 additions & 0 deletions internal/http/services/sciencemesh/sciencemesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ type config struct {
SMTPCredentials *smtpclient.SMTPCredentials `mapstructure:"smtp_credentials"`
GatewaySvc string `mapstructure:"gatewaysvc"`
MeshDirectoryURL string `mapstructure:"mesh_directory_url"`
ProviderDomain string `mapstructure:"provider_domain"`
SubjectTemplate string `mapstructure:"subject_template"`
BodyTemplatePath string `mapstructure:"body_template_path"`
}

func (c *config) init() {
Expand Down
40 changes: 22 additions & 18 deletions internal/http/services/sciencemesh/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ package sciencemesh
import (
"encoding/json"
"errors"
"fmt"
"html/template"
"mime"
"net/http"

Expand All @@ -40,6 +40,9 @@ type tokenHandler struct {
gatewayClient gateway.GatewayAPIClient
smtpCredentials *smtpclient.SMTPCredentials
meshDirectoryURL string

tplSubj *template.Template
tplBody *template.Template
}

func (h *tokenHandler) init(c *config) error {
Expand All @@ -54,6 +57,14 @@ func (h *tokenHandler) init(c *config) error {
}

h.meshDirectoryURL = c.MeshDirectoryURL

if err := h.initSubjectTemplate(c.SubjectTemplate); err != nil {
return err
}

if err := h.initBodyTemplate(c.BodyTemplatePath); err != nil {
return err
}
return nil
}

Expand All @@ -63,30 +74,23 @@ func (h *tokenHandler) init(c *config) error {
func (h *tokenHandler) Generate(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

query := r.URL.Query()
token, err := h.gatewayClient.GenerateInviteToken(ctx, &invitepb.GenerateInviteTokenRequest{
Description: r.URL.Query().Get("description"),
Description: query.Get("description"),
})
if err != nil {
reqres.WriteError(w, r, reqres.APIErrorServerError, "error generating token", err)
return
}

if r.FormValue("recipient") != "" && h.smtpCredentials != nil {
usr := ctxpkg.ContextMustGetUser(ctx)

// TODO: the message body needs to point to the meshdirectory service
subject := fmt.Sprintf("ScienceMesh: %s wants to collaborate with you", usr.DisplayName)
body := "Hi,\n\n" +
usr.DisplayName + " (" + usr.Mail + ") wants to start sharing OCM resources with you. " +
"To accept the invite, please visit the following URL:\n" +
h.meshDirectoryURL + "?token=" + token.InviteToken.Token + "&providerDomain=" + usr.Id.Idp + "\n\n" +
"Alternatively, you can visit your mesh provider and use the following details:\n" +
"Token: " + token.InviteToken.Token + "\n" +
"ProviderDomain: " + usr.Id.Idp + "\n\n" +
"Best,\nThe ScienceMesh team"

err = h.smtpCredentials.SendMail(r.FormValue("recipient"), subject, body)
if err != nil {
recipient := query.Get("recipient")
if recipient != "" && h.smtpCredentials != nil {
templObj := &emailParams{
User: ctxpkg.ContextMustGetUser(ctx),
Token: token.InviteToken.Token,
MeshDirectoryURL: h.meshDirectoryURL,
}
if err := h.sendEmail(recipient, templObj); err != nil {
reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending token by mail", err)
return
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/smtpclient/smtpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"bytes"
"encoding/base64"
"fmt"
"net/http"
"net/smtp"
"os"
"strings"
Expand Down Expand Up @@ -64,14 +65,17 @@ func NewSMTPCredentials(c *SMTPCredentials) *SMTPCredentials {

// SendMail allows sending mails using a set of client credentials.
func (creds *SMTPCredentials) SendMail(recipient, subject, body string) error {
// try to detect the content type from the subject
mime := http.DetectContentType([]byte(body))

headers := map[string]string{
"From": creds.SenderMail,
"To": recipient,
"Subject": subject,
"Date": time.Now().Format(time.RFC1123Z),
"Message-ID": uuid.New().String(),
"MIME-Version": "1.0",
"Content-Type": "text/plain; charset=\"utf-8\"",
"Content-Type": mime + "; charset=\"utf-8\"",
"Content-Transfer-Encoding": "base64",
}

Expand Down

0 comments on commit a56a9aa

Please sign in to comment.