Check license expiration, disable license and notify administrator (#14420)
* Check license expiration and notify administrator
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5ad3eaf7ee
Коммит
58305b080f
@@ -848,6 +848,7 @@ type AppIface interface {
|
||||
SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList) ([]string, error)
|
||||
SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
|
||||
SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError)
|
||||
SendRemoveExpiredLicenseEmail(email string, locale, siteURL string, licenseId string) *model.AppError
|
||||
SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError
|
||||
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
|
||||
ServePluginRequest(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
18
app/email.go
18
app/email.go
@@ -513,6 +513,24 @@ func (a *App) SendDeactivateAccountEmail(email string, locale, siteURL string) *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SendRemoveExpiredLicenseEmail(email string, locale, siteURL string, licenseId string) *model.AppError {
|
||||
T := utils.GetUserTranslations(locale)
|
||||
subject := T("api.templates.remove_expired_license.subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]})
|
||||
|
||||
bodyPage := a.newEmailTemplate("remove_expired_license", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.remove_expired_license.body.title")
|
||||
bodyPage.Props["Link"] = fmt.Sprintf("%s?id=%s", model.LICENSE_RENEWAL_LINK, licenseId)
|
||||
bodyPage.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button")
|
||||
|
||||
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) sendNotificationMail(to, subject, htmlBody string) *model.AppError {
|
||||
if !*a.Config().EmailSettings.SendEmailNotifications {
|
||||
return nil
|
||||
|
||||
@@ -158,6 +158,8 @@ func (a *App) RemoveLicense() *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
mlog.Info("Remove license.", mlog.String("id", model.SYSTEM_ACTIVE_LICENSE_ID))
|
||||
|
||||
sysVar := &model.System{}
|
||||
sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID
|
||||
sysVar.Value = ""
|
||||
|
||||
@@ -12402,6 +12402,28 @@ func (a *OpenTracingAppLayer) SendPasswordResetEmail(email string, token *model.
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SendRemoveExpiredLicenseEmail(email string, locale string, siteURL string, licenseId string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendRemoveExpiredLicenseEmail")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.SendRemoveExpiredLicenseEmail(email, locale, siteURL, licenseId)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SendSignInChangeEmail(email string, method string, locale string, siteURL string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendSignInChangeEmail")
|
||||
|
||||
@@ -348,6 +348,9 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
s.Go(func() {
|
||||
runCommandWebhookCleanupJob(s)
|
||||
})
|
||||
s.Go(func() {
|
||||
runLicenseExpirationCheckJob(s)
|
||||
})
|
||||
|
||||
if complianceI := s.Compliance; complianceI != nil {
|
||||
complianceI.StartComplianceDailyJob()
|
||||
@@ -777,6 +780,13 @@ func runSessionCleanupJob(s *Server) {
|
||||
}, time.Hour*24)
|
||||
}
|
||||
|
||||
func runLicenseExpirationCheckJob(s *Server) {
|
||||
doLicenseExpirationCheck(s)
|
||||
model.CreateRecurringTask("License Expiration Check", func() {
|
||||
doLicenseExpirationCheck(s)
|
||||
}, time.Hour*24)
|
||||
}
|
||||
|
||||
func doSecurity(s *Server) {
|
||||
s.DoSecurityUpdateCheck()
|
||||
}
|
||||
@@ -804,6 +814,46 @@ func doSessionCleanup(s *Server) {
|
||||
s.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE)
|
||||
}
|
||||
|
||||
func doLicenseExpirationCheck(s *Server) {
|
||||
s.FakeApp().LoadLicense()
|
||||
license := s.License()
|
||||
|
||||
if license == nil {
|
||||
mlog.Debug("License cannot be found.")
|
||||
return
|
||||
}
|
||||
|
||||
if !license.IsPastGracePeriod() {
|
||||
mlog.Debug("License is not past the grace period.")
|
||||
return
|
||||
}
|
||||
|
||||
users, err := s.Store.User().GetSystemAdminProfiles()
|
||||
if err != nil {
|
||||
mlog.Error("Failed to get system admins for license expired message from Mattermost.")
|
||||
return
|
||||
}
|
||||
|
||||
//send email to admin(s)
|
||||
for _, user := range users {
|
||||
user := user
|
||||
if user.Email == "" {
|
||||
mlog.Error("Invalid system admin email.", mlog.String("user_email", user.Email))
|
||||
continue
|
||||
}
|
||||
|
||||
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
|
||||
s.Go(func() {
|
||||
if err := s.FakeApp().SendRemoveExpiredLicenseEmail(user.Email, user.Locale, *s.Config().ServiceSettings.SiteURL, license.Id); err != nil {
|
||||
mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//remove the license
|
||||
s.FakeApp().RemoveLicense()
|
||||
}
|
||||
|
||||
func (s *Server) StartSearchEngine() (string, string) {
|
||||
if s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
|
||||
s.Go(func() {
|
||||
|
||||
16
i18n/en.json
16
i18n/en.json
@@ -1464,6 +1464,10 @@
|
||||
"id": "api.license.client.old_format.app_error",
|
||||
"translation": "New format for the client license is not supported yet. Please specify format=old in the query string."
|
||||
},
|
||||
{
|
||||
"id": "api.license.remove_expired_license.failed.error",
|
||||
"translation": "Failed to send the disable license email successfully."
|
||||
},
|
||||
{
|
||||
"id": "api.marshal_error",
|
||||
"translation": "marshal error"
|
||||
@@ -2298,6 +2302,18 @@
|
||||
"id": "api.templates.post_body.button",
|
||||
"translation": "Go To Post"
|
||||
},
|
||||
{
|
||||
"id": "api.templates.remove_expired_license.body.renew_button",
|
||||
"translation": "Renew License"
|
||||
},
|
||||
{
|
||||
"id": "api.templates.remove_expired_license.body.title",
|
||||
"translation": "Enterprise license has expired and some features may be disabled. Please renew."
|
||||
},
|
||||
{
|
||||
"id": "api.templates.remove_expired_license.subject",
|
||||
"translation": "Mattermost Enterprise license has been disabled."
|
||||
},
|
||||
{
|
||||
"id": "api.templates.reset_body.button",
|
||||
"translation": "Reset Password"
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
const (
|
||||
EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error"
|
||||
INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error"
|
||||
LICENSE_GRACE_PERIOD = 1000 * 60 * 60 * 24 * 10 //10 days
|
||||
LICENSE_RENEWAL_LINK = "https://licensing.mattermost.com/renew"
|
||||
)
|
||||
|
||||
type LicenseRecord struct {
|
||||
@@ -194,6 +196,11 @@ func (l *License) IsExpired() bool {
|
||||
return l.ExpiresAt < GetMillis()
|
||||
}
|
||||
|
||||
func (l *License) IsPastGracePeriod() bool {
|
||||
timeDiff := GetMillis() - l.ExpiresAt
|
||||
return timeDiff > LICENSE_GRACE_PERIOD
|
||||
}
|
||||
|
||||
func (l *License) IsStarted() bool {
|
||||
return l.StartsAt < GetMillis()
|
||||
}
|
||||
|
||||
@@ -116,6 +116,15 @@ func TestLicenseIsExpired(t *testing.T) {
|
||||
assert.False(t, l1.IsExpired())
|
||||
}
|
||||
|
||||
func TestLicenseIsPastGracePeriod(t *testing.T) {
|
||||
l1 := License{}
|
||||
l1.ExpiresAt = GetMillis() - LICENSE_GRACE_PERIOD - 1000
|
||||
assert.True(t, l1.IsPastGracePeriod())
|
||||
|
||||
l1.ExpiresAt = GetMillis() + 1000
|
||||
assert.False(t, l1.IsPastGracePeriod())
|
||||
}
|
||||
|
||||
func TestLicenseIsStarted(t *testing.T) {
|
||||
l1 := License{}
|
||||
l1.StartsAt = GetMillis() - 1000
|
||||
|
||||
50
templates/remove_expired_license.html
Обычный файл
50
templates/remove_expired_license.html
Обычный файл
@@ -0,0 +1,50 @@
|
||||
{{define "remove_expired_license"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
|
||||
style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
|
||||
style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
|
||||
style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px"
|
||||
style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0"
|
||||
style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Props.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.Link}}"
|
||||
style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.LinkButton}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
Ссылка в новой задаче
Block a user