Update server dependencies

This commit is contained in:
Hanzei 2018-09-23 08:18:35 +02:00
parent d0df44c109
commit d918262365
No known key found for this signature in database
GPG key ID: 69A2DEFD98937BA0
31 changed files with 1233 additions and 806 deletions

View file

@ -1,157 +0,0 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package utils
import (
"crypto"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyZmShlU8Z8HdG0IWSZ8r
tSyzyxrXkJjsFUf0Ke7bm/TLtIggRdqOcUF3XEWqQk5RGD5vuq7Rlg1zZqMEBk8N
EZeRhkxyaZW8pLjxwuBUOnXfJew31+gsTNdKZzRjrvPumKr3EtkleuoxNdoatu4E
HrKmR/4Yi71EqAvkhk7ZjQFuF0osSWJMEEGGCSUYQnTEqUzcZSh1BhVpkIkeu8Kk
1wCtptODixvEujgqVe+SrE3UlZjBmPjC/CL+3cYmufpSNgcEJm2mwsdaXp2OPpfn
a0v85XL6i9ote2P+fLZ3wX9EoioHzgdgB7arOxY50QRJO7OyCqpKFKv6lRWTXuSt
hwIDAQAB
-----END PUBLIC KEY-----`)
func ValidateLicense(signed []byte) (bool, string) {
decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed)))
_, err := base64.StdEncoding.Decode(decoded, signed)
if err != nil {
mlog.Error(fmt.Sprintf("Encountered error decoding license, err=%v", err.Error()))
return false, ""
}
if len(decoded) <= 256 {
mlog.Error("Signed license not long enough")
return false, ""
}
// remove null terminator
for decoded[len(decoded)-1] == byte(0) {
decoded = decoded[:len(decoded)-1]
}
plaintext := decoded[:len(decoded)-256]
signature := decoded[len(decoded)-256:]
block, _ := pem.Decode(publicKey)
public, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
mlog.Error(fmt.Sprintf("Encountered error signing license, err=%v", err.Error()))
return false, ""
}
rsaPublic := public.(*rsa.PublicKey)
h := sha512.New()
h.Write(plaintext)
d := h.Sum(nil)
err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature)
if err != nil {
mlog.Error(fmt.Sprintf("Invalid signature, err=%v", err.Error()))
return false, ""
}
return true, string(plaintext)
}
func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte) {
fileName := GetLicenseFileLocation(location)
if _, err := os.Stat(fileName); err != nil {
mlog.Debug(fmt.Sprintf("We could not find the license key in the database or on disk at %v", fileName))
return nil, nil
}
mlog.Info(fmt.Sprintf("License key has not been uploaded. Loading license key from disk at %v", fileName))
licenseBytes := GetLicenseFileFromDisk(fileName)
if success, licenseStr := ValidateLicense(licenseBytes); !success {
mlog.Error(fmt.Sprintf("Found license key at %v but it appears to be invalid.", fileName))
return nil, nil
} else {
return model.LicenseFromJson(strings.NewReader(licenseStr)), licenseBytes
}
}
func GetLicenseFileFromDisk(fileName string) []byte {
file, err := os.Open(fileName)
if err != nil {
mlog.Error(fmt.Sprintf("Failed to open license key from disk at %v err=%v", fileName, err.Error()))
return nil
}
defer file.Close()
licenseBytes, err := ioutil.ReadAll(file)
if err != nil {
mlog.Error(fmt.Sprintf("Failed to read license key from disk at %v err=%v", fileName, err.Error()))
return nil
}
return licenseBytes
}
func GetLicenseFileLocation(fileLocation string) string {
if fileLocation == "" {
configDir, _ := FindDir("config")
return filepath.Join(configDir, "mattermost.mattermost-license")
} else {
return fileLocation
}
}
func GetClientLicense(l *model.License) map[string]string {
props := make(map[string]string)
props["IsLicensed"] = strconv.FormatBool(l != nil)
if l != nil {
props["Id"] = l.Id
props["Users"] = strconv.Itoa(*l.Features.Users)
props["LDAP"] = strconv.FormatBool(*l.Features.LDAP)
props["MFA"] = strconv.FormatBool(*l.Features.MFA)
props["SAML"] = strconv.FormatBool(*l.Features.SAML)
props["Cluster"] = strconv.FormatBool(*l.Features.Cluster)
props["Metrics"] = strconv.FormatBool(*l.Features.Metrics)
props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth)
props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth)
props["Compliance"] = strconv.FormatBool(*l.Features.Compliance)
props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS)
props["Announcement"] = strconv.FormatBool(*l.Features.Announcement)
props["Elasticsearch"] = strconv.FormatBool(*l.Features.Elasticsearch)
props["DataRetention"] = strconv.FormatBool(*l.Features.DataRetention)
props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10)
props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10)
props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10)
props["Name"] = l.Customer.Name
props["Email"] = l.Customer.Email
props["Company"] = l.Customer.Company
props["PhoneNumber"] = l.Customer.PhoneNumber
props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents)
props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport)
props["CustomPermissionsSchemes"] = strconv.FormatBool(*l.Features.CustomPermissionsSchemes)
}
return props
}

View file

@ -16,27 +16,27 @@ var (
DefaultUrlSchemes = []string{"http", "https", "ftp", "mailto", "tel"}
)
// Given a string with a w at the given position, tries to parse and return a link starting with "www."
// Given a string with a w at the given position, tries to parse and return a range containing a www link.
// if one exists. If the text at the given position isn't a link, returns an empty string. Equivalent to
// www_match from the reference code.
func parseWWWAutolink(data string, position int) string {
func parseWWWAutolink(data string, position int) (Range, bool) {
// Check that this isn't part of another word
if position > 1 {
prevChar := data[position-1]
if !isWhitespaceByte(prevChar) && !isAllowedBeforeWWWLink(prevChar) {
return ""
return Range{}, false
}
}
// Check that this starts with www
if len(data)-position < 4 || !regexp.MustCompile(`^www\d{0,3}\.`).MatchString(data[position:]) {
return ""
return Range{}, false
}
end := checkDomain(data[position:], false)
if end == 0 {
return ""
return Range{}, false
}
end += position
@ -47,12 +47,12 @@ func parseWWWAutolink(data string, position int) string {
}
// Trim trailing punctuation
link := trimTrailingCharactersFromLink(data[position:end])
if link == "" {
return ""
end = trimTrailingCharactersFromLink(data, position, end)
if position == end {
return Range{}, false
}
return link
return Range{position, end}, true
}
func isAllowedBeforeWWWLink(c byte) bool {
@ -64,13 +64,13 @@ func isAllowedBeforeWWWLink(c byte) bool {
}
}
// Given a string with a : at the given position, tried to parse and return a link starting with a URL scheme
// Given a string with a : at the given position, tried to parse and return a range containing a URL scheme
// if one exists. If the text around the given position isn't a link, returns an empty string. Equivalent to
// url_match from the reference code.
func parseURLAutolink(data string, position int) string {
func parseURLAutolink(data string, position int) (Range, bool) {
// Check that a :// exists. This doesn't match the clients that treat the slashes as optional.
if len(data)-position < 4 || data[position+1] != '/' || data[position+2] != '/' {
return ""
return Range{}, false
}
start := position - 1
@ -81,12 +81,12 @@ func parseURLAutolink(data string, position int) string {
// Ensure that the URL scheme is allowed and that at least one character after the scheme is valid.
scheme := data[start:position]
if !isSchemeAllowed(scheme) || !isValidHostCharacter(data[position+3:]) {
return ""
return Range{}, false
}
end := checkDomain(data[position+3:], true)
if end == 0 {
return ""
return Range{}, false
}
end += position
@ -97,12 +97,12 @@ func parseURLAutolink(data string, position int) string {
}
// Trim trailing punctuation
link := trimTrailingCharactersFromLink(data[start:end])
if link == "" {
return ""
end = trimTrailingCharactersFromLink(data, start, end)
if start == end {
return Range{}, false
}
return link
return Range{start, end}, true
}
func isSchemeAllowed(scheme string) bool {
@ -166,9 +166,9 @@ func isValidHostCharacter(link string) bool {
}
// Removes any trailing characters such as punctuation or stray brackets that shouldn't be part of the link.
// Equivalent to autolink_delim from the reference code.
func trimTrailingCharactersFromLink(link string) string {
runes := []rune(link)
// Returns a new end position for the link. Equivalent to autolink_delim from the reference code.
func trimTrailingCharactersFromLink(markdown string, start int, end int) int {
runes := []rune(markdown[start:end])
linkEnd := len(runes)
// Cut off the link before an open angle bracket if it contains one
@ -240,7 +240,7 @@ func trimTrailingCharactersFromLink(link string) string {
}
}
return string(runes[:linkEnd])
return start + len(string(runes[:linkEnd]))
}
func canEndAutolink(c rune) bool {

View file

@ -157,7 +157,7 @@ func RenderInlineHTML(inline Inline) (result string) {
}
result += "</a>"
case *Autolink:
result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Link)) + `">`
result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`
for _, inline := range v.Children {
result += RenderInlineHTML(inline)
}

View file

@ -86,7 +86,19 @@ type Autolink struct {
Children []Inline
Link string
RawDestination Range
markdown string
}
func (i *Autolink) Destination() string {
destination := Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
if strings.HasPrefix(destination, "www") {
destination = "http://" + destination
}
return destination
}
type delimiterType int
@ -254,7 +266,7 @@ func (p *inlineParser) parseLinkOrImageDelimiter() {
}
}
func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int) (destination, title Range, end int, ok bool) {
func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int, isImage bool) (destination, title Range, end int, ok bool) {
if position >= len(p.raw) || p.raw[position] != '(' {
return
}
@ -273,6 +285,23 @@ func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int) (destin
}
position = end
if isImage && position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
dimensionsStart := nextNonWhitespace(p.raw, position)
if dimensionsStart >= len(p.raw) {
return
}
if p.raw[dimensionsStart] == '=' {
// Read optional image dimensions even if we don't use them
_, end, ok = parseImageDimensions(p.raw, dimensionsStart)
if !ok {
return
}
position = end
}
}
if position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
titleStart := nextNonWhitespace(p.raw, position)
if titleStart >= len(p.raw) {
@ -281,11 +310,13 @@ func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int) (destin
return destination, Range{titleStart, titleStart}, titleStart + 1, true
}
title, end, ok = parseLinkTitle(p.raw, titleStart)
if !ok {
return
if p.raw[titleStart] == '"' || p.raw[titleStart] == '\'' || p.raw[titleStart] == '(' {
title, end, ok = parseLinkTitle(p.raw, titleStart)
if !ok {
return
}
position = end
}
position = end
}
closingPosition := nextNonWhitespace(p.raw, position)
@ -317,9 +348,11 @@ func (p *inlineParser) lookForLinkOrImage() {
break
}
isImage := d.Type == imageOpeningDelimiter
var inline Inline
if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position + 1); ok {
if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position+1, isImage); ok {
destinationMarkdownPosition := relativeToAbsolutePosition(p.ranges, destination.Position)
linkOrImage := InlineLinkOrImage{
Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
@ -465,15 +498,18 @@ func (p *inlineParser) parseAutolink(c rune) bool {
}
}
link := ""
text := ""
var link Range
if c == ':' {
text = parseURLAutolink(p.raw, p.position)
link = text
var ok bool
link, ok = parseURLAutolink(p.raw, p.position)
if !ok {
return false
}
// Since the current position is at the colon, we have to rewind the parsing slightly so that
// we don't duplicate the URL scheme
rewind := strings.Index(text, ":")
rewind := strings.Index(p.raw[link.Position:link.End], ":")
if rewind != -1 {
lastInline := p.inlines[len(p.inlines)-1]
lastText, ok := lastInline.(*Text)
@ -491,22 +527,30 @@ func (p *inlineParser) parseAutolink(c rune) bool {
Range: Range{lastText.Range.Position, lastText.Range.End - rewind},
})
p.position -= rewind
}
} else if c == 'w' {
text = parseWWWAutolink(p.raw, p.position)
link = "http://" + text
} else if c == 'w' || c == 'W' {
var ok bool
link, ok = parseWWWAutolink(p.raw, p.position)
if !ok {
return false
}
}
if text == "" {
return false
}
linkMarkdownPosition := relativeToAbsolutePosition(p.ranges, link.Position)
linkRange := Range{linkMarkdownPosition, linkMarkdownPosition + link.End - link.Position}
p.inlines = append(p.inlines, &Autolink{
Link: link,
Children: []Inline{&Text{Text: text}},
Children: []Inline{
&Text{
Text: p.raw[link.Position:link.End],
Range: linkRange,
},
},
RawDestination: linkRange,
markdown: p.markdown,
})
p.position += len(text)
p.position += (link.End - link.Position)
return true
}

View file

@ -128,3 +128,57 @@ func parseLinkLabel(markdown string, position int) (raw Range, next int, ok bool
return
}
// As a non-standard feature, we allow image links to specify dimensions of the image by adding "=WIDTHxHEIGHT"
// after the image destination but before the image title like ![alt](http://example.com/image.png =100x200 "title").
// Both width and height are optional, but at least one of them must be specified.
func parseImageDimensions(markdown string, position int) (raw Range, next int, ok bool) {
if position >= len(markdown) {
return
}
originalPosition := position
// Read =
position += 1
if position >= len(markdown) {
return
}
// Read width
hasWidth := false
for isNumericByte(markdown[position]) {
hasWidth = true
position += 1
}
// Look for early end of dimensions
if isWhitespaceByte(markdown[position]) || markdown[position] == ')' {
return Range{originalPosition, position - 1}, position, true
}
// Read the x
if markdown[position] != 'x' && markdown[position] != 'X' {
return
}
position += 1
// Read height
hasHeight := false
for isNumericByte(markdown[position]) {
hasHeight = true
position += 1
}
// Make sure the there's no trailing characters
if !isWhitespaceByte(markdown[position]) && markdown[position] != ')' {
return
}
if !hasWidth && !hasHeight {
// At least one of width or height is required
return
}
return Range{originalPosition, position - 1}, position, true
}

View file

@ -32,8 +32,16 @@ func isWhitespaceByte(c byte) bool {
return isWhitespace(rune(c))
}
func isNumeric(c rune) bool {
return c >= '0' && c <= '9'
}
func isNumericByte(c byte) bool {
return isNumeric(rune(c))
}
func isHex(c rune) bool {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
return isNumeric(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
}
func isHexByte(c byte) bool {
@ -41,7 +49,7 @@ func isHexByte(c byte) bool {
}
func isAlphanumeric(c rune) bool {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
return isNumeric(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
func isAlphanumericByte(c byte) bool {