add support to pass access-token for e-mail publishing
This commit is contained in:
		
							parent
							
								
									5f75e98861
								
							
						
					
					
						commit
						e892b994c3
					
				
					 3 changed files with 40 additions and 0 deletions
				
			
		|  | @ -2582,6 +2582,11 @@ format is: | ||||||
| ntfy-$topic@ntfy.sh | ntfy-$topic@ntfy.sh | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, e-mail publishing won't work without providing an authorized access token. That will change the format of the e-mail's recipient address to | ||||||
|  | ``` | ||||||
|  | ntfy-$topic+$token@ntfy.sh | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| As of today, e-mail publishing only supports adding a [message title](#message-title) (the e-mail subject). Tags, priority, | As of today, e-mail publishing only supports adding a [message title](#message-title) (the e-mail subject). Tags, priority, | ||||||
| delay and other features are not supported (yet). Here's an example that will publish a message with the  | delay and other features are not supported (yet). Here's an example that will publish a message with the  | ||||||
| title `You've Got Mail` to topic `sometopic` (see [ntfy.sh/sometopic](https://ntfy.sh/sometopic)): | title `You've Got Mail` to topic `sometopic` (see [ntfy.sh/sometopic](https://ntfy.sh/sometopic)): | ||||||
|  |  | ||||||
|  | @ -65,6 +65,7 @@ type smtpSession struct { | ||||||
| 	backend *smtpBackend | 	backend *smtpBackend | ||||||
| 	conn    *smtp.Conn | 	conn    *smtp.Conn | ||||||
| 	topic   string | 	topic   string | ||||||
|  | 	token   string | ||||||
| 	mu      sync.Mutex | 	mu      sync.Mutex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -81,6 +82,7 @@ func (s *smtpSession) Mail(from string, opts *smtp.MailOptions) error { | ||||||
| func (s *smtpSession) Rcpt(to string) error { | func (s *smtpSession) Rcpt(to string) error { | ||||||
| 	logem(s.conn).Field("smtp_rcpt_to", to).Debug("RCPT TO: %s", to) | 	logem(s.conn).Field("smtp_rcpt_to", to).Debug("RCPT TO: %s", to) | ||||||
| 	return s.withFailCount(func() error { | 	return s.withFailCount(func() error { | ||||||
|  | 		token := "" | ||||||
| 		conf := s.backend.config | 		conf := s.backend.config | ||||||
| 		addressList, err := mail.ParseAddressList(to) | 		addressList, err := mail.ParseAddressList(to) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -92,18 +94,27 @@ func (s *smtpSession) Rcpt(to string) error { | ||||||
| 		if !strings.HasSuffix(to, "@"+conf.SMTPServerDomain) { | 		if !strings.HasSuffix(to, "@"+conf.SMTPServerDomain) { | ||||||
| 			return errInvalidDomain | 			return errInvalidDomain | ||||||
| 		} | 		} | ||||||
|  | 		// remove @ntfy.sh from end of email | ||||||
| 		to = strings.TrimSuffix(to, "@"+conf.SMTPServerDomain) | 		to = strings.TrimSuffix(to, "@"+conf.SMTPServerDomain) | ||||||
| 		if conf.SMTPServerAddrPrefix != "" { | 		if conf.SMTPServerAddrPrefix != "" { | ||||||
| 			if !strings.HasPrefix(to, conf.SMTPServerAddrPrefix) { | 			if !strings.HasPrefix(to, conf.SMTPServerAddrPrefix) { | ||||||
| 				return errInvalidAddress | 				return errInvalidAddress | ||||||
| 			} | 			} | ||||||
|  | 			// remove ntfy- from beginning of email | ||||||
| 			to = strings.TrimPrefix(to, conf.SMTPServerAddrPrefix) | 			to = strings.TrimPrefix(to, conf.SMTPServerAddrPrefix) | ||||||
| 		} | 		} | ||||||
|  | 		// if email contains token, split topic and token | ||||||
|  | 		if strings.Contains(to, "+") { | ||||||
|  | 			parts := strings.Split(to, "+") | ||||||
|  | 			to = parts[0] | ||||||
|  | 			token = parts[1] | ||||||
|  | 		} | ||||||
| 		if !topicRegex.MatchString(to) { | 		if !topicRegex.MatchString(to) { | ||||||
| 			return errInvalidTopic | 			return errInvalidTopic | ||||||
| 		} | 		} | ||||||
| 		s.mu.Lock() | 		s.mu.Lock() | ||||||
| 		s.topic = to | 		s.topic = to | ||||||
|  | 		s.token = token | ||||||
| 		s.mu.Unlock() | 		s.mu.Unlock() | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  | @ -177,6 +188,9 @@ func (s *smtpSession) publishMessage(m *message) error { | ||||||
| 	if m.Title != "" { | 	if m.Title != "" { | ||||||
| 		req.Header.Set("Title", m.Title) | 		req.Header.Set("Title", m.Title) | ||||||
| 	} | 	} | ||||||
|  | 	if s.token != "" { | ||||||
|  | 		req.Header.Add("Authorization", "Bearer "+s.token) | ||||||
|  | 	} | ||||||
| 	rr := httptest.NewRecorder() | 	rr := httptest.NewRecorder() | ||||||
| 	s.backend.handler(rr, req) | 	s.backend.handler(rr, req) | ||||||
| 	if rr.Code != http.StatusOK { | 	if rr.Code != http.StatusOK { | ||||||
|  |  | ||||||
|  | @ -492,6 +492,27 @@ L0VOIj4KClRoaXMgaXMgYSB0ZXN0IG1lc3NhZ2UgZnJvbSBUcnVlTkFTIENPUkUuCg== | ||||||
| 	writeAndReadUntilLine(t, email, c, scanner, "554 5.0.0 Error: transaction failed, blame it on the weather: multipart message nested too deep") | 	writeAndReadUntilLine(t, email, c, scanner, "554 5.0.0 Error: transaction failed, blame it on the weather: multipart message nested too deep") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestSmtpBackend_PlaintextWithToken(t *testing.T) { | ||||||
|  | 	email := `EHLO example.com | ||||||
|  | MAIL FROM: phil@example.com | ||||||
|  | RCPT TO: ntfy-mytopic+tk_KLORUqSqvNRLpY11DfkHVbHu9NGG2@ntfy.sh | ||||||
|  | DATA | ||||||
|  | Subject: Very short mail | ||||||
|  | 
 | ||||||
|  | what's up | ||||||
|  | . | ||||||
|  | ` | ||||||
|  | 	s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		require.Equal(t, "/mytopic", r.URL.Path) | ||||||
|  | 		require.Equal(t, "Very short mail", r.Header.Get("Title")) | ||||||
|  | 		require.Equal(t, "Bearer tk_KLORUqSqvNRLpY11DfkHVbHu9NGG2", r.Header.Get("Authorization")) | ||||||
|  | 		require.Equal(t, "what's up", readAll(t, r.Body)) | ||||||
|  | 	}) | ||||||
|  | 	defer s.Close() | ||||||
|  | 	defer c.Close() | ||||||
|  | 	writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type smtpHandlerFunc func(http.ResponseWriter, *http.Request) | type smtpHandlerFunc func(http.ResponseWriter, *http.Request) | ||||||
| 
 | 
 | ||||||
| func newTestSMTPServer(t *testing.T, handler smtpHandlerFunc) (s *smtp.Server, c net.Conn, conf *Config, scanner *bufio.Scanner) { | func newTestSMTPServer(t *testing.T, handler smtpHandlerFunc) (s *smtp.Server, c net.Conn, conf *Config, scanner *bufio.Scanner) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue