diff options
author | techknowlogick <matti@mdranta.net> | 2019-02-05 11:52:51 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-05 11:52:51 -0500 |
commit | 9de871a0f8911030f8e06a881803cf722b8798ea (patch) | |
tree | 206400f0a5873d7d078fcdd004956036f07a1db5 /vendor/github.com/couchbase/goutils | |
parent | bf4badad1d68c18d7ffb92c69e09e4e8aa252935 (diff) | |
download | gitea-9de871a0f8911030f8e06a881803cf722b8798ea.tar.gz gitea-9de871a0f8911030f8e06a881803cf722b8798ea.zip |
add other session providers (#5963)
Diffstat (limited to 'vendor/github.com/couchbase/goutils')
5 files changed, 1305 insertions, 0 deletions
diff --git a/vendor/github.com/couchbase/goutils/LICENSE.md b/vendor/github.com/couchbase/goutils/LICENSE.md new file mode 100644 index 0000000000..a572e246e6 --- /dev/null +++ b/vendor/github.com/couchbase/goutils/LICENSE.md @@ -0,0 +1,47 @@ +COUCHBASE INC. COMMUNITY EDITION LICENSE AGREEMENT + +IMPORTANT-READ CAREFULLY: BY CLICKING THE "I ACCEPT" BOX OR INSTALLING, +DOWNLOADING OR OTHERWISE USING THIS SOFTWARE AND ANY ASSOCIATED +DOCUMENTATION, YOU, ON BEHALF OF YOURSELF OR AS AN AUTHORIZED +REPRESENTATIVE ON BEHALF OF AN ENTITY ("LICENSEE") AGREE TO ALL THE +TERMS OF THIS COMMUNITY EDITION LICENSE AGREEMENT (THE "AGREEMENT") +REGARDING YOUR USE OF THE SOFTWARE. YOU REPRESENT AND WARRANT THAT YOU +HAVE FULL LEGAL AUTHORITY TO BIND THE LICENSEE TO THIS AGREEMENT. IF YOU +DO NOT AGREE WITH ALL OF THESE TERMS, DO NOT SELECT THE "I ACCEPT" BOX +AND DO NOT INSTALL, DOWNLOAD OR OTHERWISE USE THE SOFTWARE. THE +EFFECTIVE DATE OF THIS AGREEMENT IS THE DATE ON WHICH YOU CLICK "I +ACCEPT" OR OTHERWISE INSTALL, DOWNLOAD OR USE THE SOFTWARE. + +1. License Grant. Couchbase Inc. hereby grants Licensee, free of charge, +the non-exclusive right to use, copy, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to Licensee including +the following copyright notice in all copies or substantial portions of +the Software: + +Couchbase (r) http://www.Couchbase.com Copyright 2016 Couchbase, Inc. + +As used in this Agreement, "Software" means the object code version of +the applicable elastic data management server software provided by +Couchbase Inc. + +2. Restrictions. Licensee will not reverse engineer, disassemble, or +decompile the Software (except to the extent such restrictions are +prohibited by law). + +3. Support. Couchbase, Inc. will provide Licensee with access to, and +use of, the Couchbase, Inc. support forum available at the following +URL: http://www.couchbase.org/forums/. Couchbase, Inc. may, at its +discretion, modify, suspend or terminate support at any time upon notice +to Licensee. + +4. Warranty Disclaimer and Limitation of Liability. THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +COUCHBASE INC. OR THE AUTHORS OR COPYRIGHT HOLDERS IN THE SOFTWARE BE +LIABLE FOR ANY CLAIM, DAMAGES (IINCLUDING, WITHOUT LIMITATION, DIRECT, +INDIRECT OR CONSEQUENTIAL DAMAGES) OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/couchbase/goutils/logging/logger.go b/vendor/github.com/couchbase/goutils/logging/logger.go new file mode 100644 index 0000000000..b9948f9b2e --- /dev/null +++ b/vendor/github.com/couchbase/goutils/logging/logger.go @@ -0,0 +1,481 @@ +// Copyright (c) 2016 Couchbase, Inc. +// 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. + +package logging + +import ( + "os" + "runtime" + "strings" + "sync" +) + +type Level int + +const ( + NONE = Level(iota) // Disable all logging + FATAL // System is in severe error state and has to abort + SEVERE // System is in severe error state and cannot recover reliably + ERROR // System is in error state but can recover and continue reliably + WARN // System approaching error state, or is in a correct but undesirable state + INFO // System-level events and status, in correct states + REQUEST // Request-level events, with request-specific rlevel + TRACE // Trace detailed system execution, e.g. function entry / exit + DEBUG // Debug +) + +type LogEntryFormatter int + +const ( + TEXTFORMATTER = LogEntryFormatter(iota) + JSONFORMATTER + KVFORMATTER +) + +func (level Level) String() string { + return _LEVEL_NAMES[level] +} + +var _LEVEL_NAMES = []string{ + DEBUG: "DEBUG", + TRACE: "TRACE", + REQUEST: "REQUEST", + INFO: "INFO", + WARN: "WARN", + ERROR: "ERROR", + SEVERE: "SEVERE", + FATAL: "FATAL", + NONE: "NONE", +} + +var _LEVEL_MAP = map[string]Level{ + "debug": DEBUG, + "trace": TRACE, + "request": REQUEST, + "info": INFO, + "warn": WARN, + "error": ERROR, + "severe": SEVERE, + "fatal": FATAL, + "none": NONE, +} + +func ParseLevel(name string) (level Level, ok bool) { + level, ok = _LEVEL_MAP[strings.ToLower(name)] + return +} + +/* + +Pair supports logging of key-value pairs. Keys beginning with _ are +reserved for the logger, e.g. _time, _level, _msg, and _rlevel. The +Pair APIs are designed to avoid heap allocation and garbage +collection. + +*/ +type Pairs []Pair +type Pair struct { + Name string + Value interface{} +} + +/* + +Map allows key-value pairs to be specified using map literals or data +structures. For example: + +Errorm(msg, Map{...}) + +Map incurs heap allocation and garbage collection, so the Pair APIs +should be preferred. + +*/ +type Map map[string]interface{} + +// Logger provides a common interface for logging libraries +type Logger interface { + /* + These APIs write all the given pairs in addition to standard logger keys. + */ + Logp(level Level, msg string, kv ...Pair) + + Debugp(msg string, kv ...Pair) + + Tracep(msg string, kv ...Pair) + + Requestp(rlevel Level, msg string, kv ...Pair) + + Infop(msg string, kv ...Pair) + + Warnp(msg string, kv ...Pair) + + Errorp(msg string, kv ...Pair) + + Severep(msg string, kv ...Pair) + + Fatalp(msg string, kv ...Pair) + + /* + These APIs write the fields in the given kv Map in addition to standard logger keys. + */ + Logm(level Level, msg string, kv Map) + + Debugm(msg string, kv Map) + + Tracem(msg string, kv Map) + + Requestm(rlevel Level, msg string, kv Map) + + Infom(msg string, kv Map) + + Warnm(msg string, kv Map) + + Errorm(msg string, kv Map) + + Severem(msg string, kv Map) + + Fatalm(msg string, kv Map) + + /* + + These APIs only write _msg, _time, _level, and other logger keys. If + the msg contains other fields, use the Pair or Map APIs instead. + + */ + Logf(level Level, fmt string, args ...interface{}) + + Debugf(fmt string, args ...interface{}) + + Tracef(fmt string, args ...interface{}) + + Requestf(rlevel Level, fmt string, args ...interface{}) + + Infof(fmt string, args ...interface{}) + + Warnf(fmt string, args ...interface{}) + + Errorf(fmt string, args ...interface{}) + + Severef(fmt string, args ...interface{}) + + Fatalf(fmt string, args ...interface{}) + + /* + These APIs control the logging level + */ + + SetLevel(Level) // Set the logging level + + Level() Level // Get the current logging level +} + +var logger Logger = nil +var curLevel Level = DEBUG // initially set to never skip + +var loggerMutex sync.RWMutex + +// All the methods below first acquire the mutex (mostly in exclusive mode) +// and only then check if logging at the current level is enabled. +// This introduces a fair bottleneck for those log entries that should be +// skipped (the majority, at INFO or below levels) +// We try to predict here if we should lock the mutex at all by caching +// the current log level: while dynamically changing logger, there might +// be the odd entry skipped as the new level is cached. +// Since we seem to never change the logger, this is not an issue. +func skipLogging(level Level) bool { + if logger == nil { + return true + } + return level > curLevel +} + +func SetLogger(newLogger Logger) { + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger = newLogger + if logger == nil { + curLevel = NONE + } else { + curLevel = newLogger.Level() + } +} + +func Logp(level Level, msg string, kv ...Pair) { + if skipLogging(level) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Logp(level, msg, kv...) +} + +func Debugp(msg string, kv ...Pair) { + if skipLogging(DEBUG) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Debugp(msg, kv...) +} + +func Tracep(msg string, kv ...Pair) { + if skipLogging(TRACE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Tracep(msg, kv...) +} + +func Requestp(rlevel Level, msg string, kv ...Pair) { + if skipLogging(REQUEST) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Requestp(rlevel, msg, kv...) +} + +func Infop(msg string, kv ...Pair) { + if skipLogging(INFO) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Infop(msg, kv...) +} + +func Warnp(msg string, kv ...Pair) { + if skipLogging(WARN) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Warnp(msg, kv...) +} + +func Errorp(msg string, kv ...Pair) { + if skipLogging(ERROR) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Errorp(msg, kv...) +} + +func Severep(msg string, kv ...Pair) { + if skipLogging(SEVERE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Severep(msg, kv...) +} + +func Fatalp(msg string, kv ...Pair) { + if skipLogging(FATAL) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Fatalp(msg, kv...) +} + +func Logm(level Level, msg string, kv Map) { + if skipLogging(level) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Logm(level, msg, kv) +} + +func Debugm(msg string, kv Map) { + if skipLogging(DEBUG) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Debugm(msg, kv) +} + +func Tracem(msg string, kv Map) { + if skipLogging(TRACE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Tracem(msg, kv) +} + +func Requestm(rlevel Level, msg string, kv Map) { + if skipLogging(REQUEST) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Requestm(rlevel, msg, kv) +} + +func Infom(msg string, kv Map) { + if skipLogging(INFO) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Infom(msg, kv) +} + +func Warnm(msg string, kv Map) { + if skipLogging(WARN) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Warnm(msg, kv) +} + +func Errorm(msg string, kv Map) { + if skipLogging(ERROR) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Errorm(msg, kv) +} + +func Severem(msg string, kv Map) { + if skipLogging(SEVERE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Severem(msg, kv) +} + +func Fatalm(msg string, kv Map) { + if skipLogging(FATAL) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Fatalm(msg, kv) +} + +func Logf(level Level, fmt string, args ...interface{}) { + if skipLogging(level) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Logf(level, fmt, args...) +} + +func Debugf(fmt string, args ...interface{}) { + if skipLogging(DEBUG) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Debugf(fmt, args...) +} + +func Tracef(fmt string, args ...interface{}) { + if skipLogging(TRACE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Tracef(fmt, args...) +} + +func Requestf(rlevel Level, fmt string, args ...interface{}) { + if skipLogging(REQUEST) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Requestf(rlevel, fmt, args...) +} + +func Infof(fmt string, args ...interface{}) { + if skipLogging(INFO) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Infof(fmt, args...) +} + +func Warnf(fmt string, args ...interface{}) { + if skipLogging(WARN) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Warnf(fmt, args...) +} + +func Errorf(fmt string, args ...interface{}) { + if skipLogging(ERROR) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Errorf(fmt, args...) +} + +func Severef(fmt string, args ...interface{}) { + if skipLogging(SEVERE) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Severef(fmt, args...) +} + +func Fatalf(fmt string, args ...interface{}) { + if skipLogging(FATAL) { + return + } + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Fatalf(fmt, args...) +} + +func SetLevel(level Level) { + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.SetLevel(level) + curLevel = level +} + +func LogLevel() Level { + loggerMutex.RLock() + defer loggerMutex.RUnlock() + return logger.Level() +} + +func Stackf(level Level, fmt string, args ...interface{}) { + if skipLogging(level) { + return + } + buf := make([]byte, 1<<16) + n := runtime.Stack(buf, false) + s := string(buf[0:n]) + loggerMutex.Lock() + defer loggerMutex.Unlock() + logger.Logf(level, fmt, args...) + logger.Logf(level, s) +} + +func init() { + logger = NewLogger(os.Stderr, INFO, TEXTFORMATTER) + SetLogger(logger) +} diff --git a/vendor/github.com/couchbase/goutils/logging/logger_golog.go b/vendor/github.com/couchbase/goutils/logging/logger_golog.go new file mode 100644 index 0000000000..eec432a513 --- /dev/null +++ b/vendor/github.com/couchbase/goutils/logging/logger_golog.go @@ -0,0 +1,318 @@ +// Copyright (c) 2016 Couchbase, Inc. +// 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. + +package logging + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "time" +) + +type goLogger struct { + logger *log.Logger + level Level + entryFormatter formatter +} + +const ( + _LEVEL = "_level" + _MSG = "_msg" + _TIME = "_time" + _RLEVEL = "_rlevel" +) + +func NewLogger(out io.Writer, lvl Level, fmtLogging LogEntryFormatter) *goLogger { + logger := &goLogger{ + logger: log.New(out, "", 0), + level: lvl, + } + if fmtLogging == JSONFORMATTER { + logger.entryFormatter = &jsonFormatter{} + } else if fmtLogging == KVFORMATTER { + logger.entryFormatter = &keyvalueFormatter{} + } else { + logger.entryFormatter = &textFormatter{} + } + return logger +} + +func (gl *goLogger) Logp(level Level, msg string, kv ...Pair) { + if gl.logger == nil { + return + } + if level <= gl.level { + e := newLogEntry(msg, level) + copyPairs(e, kv) + gl.log(e) + } +} + +func (gl *goLogger) Debugp(msg string, kv ...Pair) { + gl.Logp(DEBUG, msg, kv...) +} + +func (gl *goLogger) Tracep(msg string, kv ...Pair) { + gl.Logp(TRACE, msg, kv...) +} + +func (gl *goLogger) Requestp(rlevel Level, msg string, kv ...Pair) { + if gl.logger == nil { + return + } + if REQUEST <= gl.level { + e := newLogEntry(msg, REQUEST) + e.Rlevel = rlevel + copyPairs(e, kv) + gl.log(e) + } +} + +func (gl *goLogger) Infop(msg string, kv ...Pair) { + gl.Logp(INFO, msg, kv...) +} + +func (gl *goLogger) Warnp(msg string, kv ...Pair) { + gl.Logp(WARN, msg, kv...) +} + +func (gl *goLogger) Errorp(msg string, kv ...Pair) { + gl.Logp(ERROR, msg, kv...) +} + +func (gl *goLogger) Severep(msg string, kv ...Pair) { + gl.Logp(SEVERE, msg, kv...) +} + +func (gl *goLogger) Fatalp(msg string, kv ...Pair) { + gl.Logp(FATAL, msg, kv...) +} + +func (gl *goLogger) Logm(level Level, msg string, kv Map) { + if gl.logger == nil { + return + } + if level <= gl.level { + e := newLogEntry(msg, level) + e.Data = kv + gl.log(e) + } +} + +func (gl *goLogger) Debugm(msg string, kv Map) { + gl.Logm(DEBUG, msg, kv) +} + +func (gl *goLogger) Tracem(msg string, kv Map) { + gl.Logm(TRACE, msg, kv) +} + +func (gl *goLogger) Requestm(rlevel Level, msg string, kv Map) { + if gl.logger == nil { + return + } + if REQUEST <= gl.level { + e := newLogEntry(msg, REQUEST) + e.Rlevel = rlevel + e.Data = kv + gl.log(e) + } +} + +func (gl *goLogger) Infom(msg string, kv Map) { + gl.Logm(INFO, msg, kv) +} + +func (gl *goLogger) Warnm(msg string, kv Map) { + gl.Logm(WARN, msg, kv) +} + +func (gl *goLogger) Errorm(msg string, kv Map) { + gl.Logm(ERROR, msg, kv) +} + +func (gl *goLogger) Severem(msg string, kv Map) { + gl.Logm(SEVERE, msg, kv) +} + +func (gl *goLogger) Fatalm(msg string, kv Map) { + gl.Logm(FATAL, msg, kv) +} + +func (gl *goLogger) Logf(level Level, format string, args ...interface{}) { + if gl.logger == nil { + return + } + if level <= gl.level { + e := newLogEntry(fmt.Sprintf(format, args...), level) + gl.log(e) + } +} + +func (gl *goLogger) Debugf(format string, args ...interface{}) { + gl.Logf(DEBUG, format, args...) +} + +func (gl *goLogger) Tracef(format string, args ...interface{}) { + gl.Logf(TRACE, format, args...) +} + +func (gl *goLogger) Requestf(rlevel Level, format string, args ...interface{}) { + if gl.logger == nil { + return + } + if REQUEST <= gl.level { + e := newLogEntry(fmt.Sprintf(format, args...), REQUEST) + e.Rlevel = rlevel + gl.log(e) + } +} + +func (gl *goLogger) Infof(format string, args ...interface{}) { + gl.Logf(INFO, format, args...) +} + +func (gl *goLogger) Warnf(format string, args ...interface{}) { + gl.Logf(WARN, format, args...) +} + +func (gl *goLogger) Errorf(format string, args ...interface{}) { + gl.Logf(ERROR, format, args...) +} + +func (gl *goLogger) Severef(format string, args ...interface{}) { + gl.Logf(SEVERE, format, args...) +} + +func (gl *goLogger) Fatalf(format string, args ...interface{}) { + gl.Logf(FATAL, format, args...) +} + +func (gl *goLogger) Level() Level { + return gl.level +} + +func (gl *goLogger) SetLevel(level Level) { + gl.level = level +} + +func (gl *goLogger) log(newEntry *logEntry) { + s := gl.entryFormatter.format(newEntry) + gl.logger.Print(s) +} + +type logEntry struct { + Time string + Level Level + Rlevel Level + Message string + Data Map +} + +func newLogEntry(msg string, level Level) *logEntry { + return &logEntry{ + Time: time.Now().Format("2006-01-02T15:04:05.000-07:00"), // time.RFC3339 with milliseconds + Level: level, + Rlevel: NONE, + Message: msg, + } +} + +func copyPairs(newEntry *logEntry, pairs []Pair) { + newEntry.Data = make(Map, len(pairs)) + for _, p := range pairs { + newEntry.Data[p.Name] = p.Value + } +} + +type formatter interface { + format(*logEntry) string +} + +type textFormatter struct { +} + +// ex. 2016-02-10T09:15:25.498-08:00 [INFO] This is a message from test in text format + +func (*textFormatter) format(newEntry *logEntry) string { + b := &bytes.Buffer{} + appendValue(b, newEntry.Time) + if newEntry.Rlevel != NONE { + fmt.Fprintf(b, "[%s,%s] ", newEntry.Level.String(), newEntry.Rlevel.String()) + } else { + fmt.Fprintf(b, "[%s] ", newEntry.Level.String()) + } + appendValue(b, newEntry.Message) + for key, value := range newEntry.Data { + appendKeyValue(b, key, value) + } + b.WriteByte('\n') + s := bytes.NewBuffer(b.Bytes()) + return s.String() +} + +func appendValue(b *bytes.Buffer, value interface{}) { + if _, ok := value.(string); ok { + fmt.Fprintf(b, "%s ", value) + } else { + fmt.Fprintf(b, "%v ", value) + } +} + +type keyvalueFormatter struct { +} + +// ex. _time=2016-02-10T09:15:25.498-08:00 _level=INFO _msg=This is a message from test in key-value format + +func (*keyvalueFormatter) format(newEntry *logEntry) string { + b := &bytes.Buffer{} + appendKeyValue(b, _TIME, newEntry.Time) + appendKeyValue(b, _LEVEL, newEntry.Level.String()) + if newEntry.Rlevel != NONE { + appendKeyValue(b, _RLEVEL, newEntry.Rlevel.String()) + } + appendKeyValue(b, _MSG, newEntry.Message) + for key, value := range newEntry.Data { + appendKeyValue(b, key, value) + } + b.WriteByte('\n') + s := bytes.NewBuffer(b.Bytes()) + return s.String() +} + +func appendKeyValue(b *bytes.Buffer, key, value interface{}) { + if _, ok := value.(string); ok { + fmt.Fprintf(b, "%v=%s ", key, value) + } else { + fmt.Fprintf(b, "%v=%v ", key, value) + } +} + +type jsonFormatter struct { +} + +// ex. {"_level":"INFO","_msg":"This is a message from test in json format","_time":"2016-02-10T09:12:59.518-08:00"} + +func (*jsonFormatter) format(newEntry *logEntry) string { + if newEntry.Data == nil { + newEntry.Data = make(Map, 5) + } + newEntry.Data[_TIME] = newEntry.Time + newEntry.Data[_LEVEL] = newEntry.Level.String() + if newEntry.Rlevel != NONE { + newEntry.Data[_RLEVEL] = newEntry.Rlevel.String() + } + newEntry.Data[_MSG] = newEntry.Message + serialized, _ := json.Marshal(newEntry.Data) + s := bytes.NewBuffer(append(serialized, '\n')) + return s.String() +} diff --git a/vendor/github.com/couchbase/goutils/scramsha/scramsha.go b/vendor/github.com/couchbase/goutils/scramsha/scramsha.go new file mode 100644 index 0000000000..b234bfc8a9 --- /dev/null +++ b/vendor/github.com/couchbase/goutils/scramsha/scramsha.go @@ -0,0 +1,207 @@ +// @author Couchbase <info@couchbase.com> +// @copyright 2018 Couchbase, Inc. +// +// 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. + +// Package scramsha provides implementation of client side SCRAM-SHA +// according to https://tools.ietf.org/html/rfc5802 +package scramsha + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "fmt" + "github.com/pkg/errors" + "golang.org/x/crypto/pbkdf2" + "hash" + "strconv" + "strings" +) + +func hmacHash(message []byte, secret []byte, hashFunc func() hash.Hash) []byte { + h := hmac.New(hashFunc, secret) + h.Write(message) + return h.Sum(nil) +} + +func shaHash(message []byte, hashFunc func() hash.Hash) []byte { + h := hashFunc() + h.Write(message) + return h.Sum(nil) +} + +func generateClientNonce(size int) (string, error) { + randomBytes := make([]byte, size) + _, err := rand.Read(randomBytes) + if err != nil { + return "", errors.Wrap(err, "Unable to generate nonce") + } + return base64.StdEncoding.EncodeToString(randomBytes), nil +} + +// ScramSha provides context for SCRAM-SHA handling +type ScramSha struct { + hashSize int + hashFunc func() hash.Hash + clientNonce string + serverNonce string + salt []byte + i int + saltedPassword []byte + authMessage string +} + +var knownMethods = []string{"SCRAM-SHA512", "SCRAM-SHA256", "SCRAM-SHA1"} + +// BestMethod returns SCRAM-SHA method we consider the best out of suggested +// by server +func BestMethod(methods string) (string, error) { + for _, m := range knownMethods { + if strings.Index(methods, m) != -1 { + return m, nil + } + } + return "", errors.Errorf( + "None of the server suggested methods [%s] are supported", + methods) +} + +// NewScramSha creates context for SCRAM-SHA handling +func NewScramSha(method string) (*ScramSha, error) { + s := &ScramSha{} + + if method == knownMethods[0] { + s.hashFunc = sha512.New + s.hashSize = 64 + } else if method == knownMethods[1] { + s.hashFunc = sha256.New + s.hashSize = 32 + } else if method == knownMethods[2] { + s.hashFunc = sha1.New + s.hashSize = 20 + } else { + return nil, errors.Errorf("Unsupported method %s", method) + } + return s, nil +} + +// GetStartRequest builds start SCRAM-SHA request to be sent to server +func (s *ScramSha) GetStartRequest(user string) (string, error) { + var err error + s.clientNonce, err = generateClientNonce(24) + if err != nil { + return "", errors.Wrapf(err, "Unable to generate SCRAM-SHA "+ + "start request for user %s", user) + } + + message := fmt.Sprintf("n,,n=%s,r=%s", user, s.clientNonce) + s.authMessage = message[3:] + return message, nil +} + +// HandleStartResponse handles server response on start SCRAM-SHA request +func (s *ScramSha) HandleStartResponse(response string) error { + parts := strings.Split(response, ",") + if len(parts) != 3 { + return errors.Errorf("expected 3 fields in first SCRAM-SHA-1 "+ + "server message %s", response) + } + if !strings.HasPrefix(parts[0], "r=") || len(parts[0]) < 3 { + return errors.Errorf("Server sent an invalid nonce %s", + parts[0]) + } + if !strings.HasPrefix(parts[1], "s=") || len(parts[1]) < 3 { + return errors.Errorf("Server sent an invalid salt %s", parts[1]) + } + if !strings.HasPrefix(parts[2], "i=") || len(parts[2]) < 3 { + return errors.Errorf("Server sent an invalid iteration count %s", + parts[2]) + } + + s.serverNonce = parts[0][2:] + encodedSalt := parts[1][2:] + var err error + s.i, err = strconv.Atoi(parts[2][2:]) + if err != nil { + return errors.Errorf("Iteration count %s must be integer.", + parts[2][2:]) + } + + if s.i < 1 { + return errors.New("Iteration count should be positive") + } + + if !strings.HasPrefix(s.serverNonce, s.clientNonce) { + return errors.Errorf("Server nonce %s doesn't contain client"+ + " nonce %s", s.serverNonce, s.clientNonce) + } + + s.salt, err = base64.StdEncoding.DecodeString(encodedSalt) + if err != nil { + return errors.Wrapf(err, "Unable to decode salt %s", + encodedSalt) + } + + s.authMessage = s.authMessage + "," + response + return nil +} + +// GetFinalRequest builds final SCRAM-SHA request to be sent to server +func (s *ScramSha) GetFinalRequest(pass string) string { + clientFinalMessageBare := "c=biws,r=" + s.serverNonce + s.authMessage = s.authMessage + "," + clientFinalMessageBare + + s.saltedPassword = pbkdf2.Key([]byte(pass), s.salt, s.i, + s.hashSize, s.hashFunc) + + clientKey := hmacHash([]byte("Client Key"), s.saltedPassword, s.hashFunc) + storedKey := shaHash(clientKey, s.hashFunc) + clientSignature := hmacHash([]byte(s.authMessage), storedKey, s.hashFunc) + + clientProof := make([]byte, len(clientSignature)) + for i := 0; i < len(clientSignature); i++ { + clientProof[i] = clientKey[i] ^ clientSignature[i] + } + + return clientFinalMessageBare + ",p=" + + base64.StdEncoding.EncodeToString(clientProof) +} + +// HandleFinalResponse handles server's response on final SCRAM-SHA request +func (s *ScramSha) HandleFinalResponse(response string) error { + if strings.Contains(response, ",") || + !strings.HasPrefix(response, "v=") { + return errors.Errorf("Server sent an invalid final message %s", + response) + } + + decodedMessage, err := base64.StdEncoding.DecodeString(response[2:]) + if err != nil { + return errors.Wrapf(err, "Unable to decode server message %s", + response[2:]) + } + serverKey := hmacHash([]byte("Server Key"), s.saltedPassword, + s.hashFunc) + serverSignature := hmacHash([]byte(s.authMessage), serverKey, + s.hashFunc) + if string(decodedMessage) != string(serverSignature) { + return errors.Errorf("Server proof %s doesn't match "+ + "the expected: %s", + string(decodedMessage), string(serverSignature)) + } + return nil +} diff --git a/vendor/github.com/couchbase/goutils/scramsha/scramsha_http.go b/vendor/github.com/couchbase/goutils/scramsha/scramsha_http.go new file mode 100644 index 0000000000..19f32b3134 --- /dev/null +++ b/vendor/github.com/couchbase/goutils/scramsha/scramsha_http.go @@ -0,0 +1,252 @@ +// @author Couchbase <info@couchbase.com> +// @copyright 2018 Couchbase, Inc. +// +// 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. + +// Package scramsha provides implementation of client side SCRAM-SHA +// via Http according to https://tools.ietf.org/html/rfc7804 +package scramsha + +import ( + "encoding/base64" + "github.com/pkg/errors" + "io" + "io/ioutil" + "net/http" + "strings" +) + +// consts used to parse scramsha response from target +const ( + WWWAuthenticate = "WWW-Authenticate" + AuthenticationInfo = "Authentication-Info" + Authorization = "Authorization" + DataPrefix = "data=" + SidPrefix = "sid=" +) + +// Request provides implementation of http request that can be retried +type Request struct { + body io.ReadSeeker + + // Embed an HTTP request directly. This makes a *Request act exactly + // like an *http.Request so that all meta methods are supported. + *http.Request +} + +type lenReader interface { + Len() int +} + +// NewRequest creates http request that can be retried +func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) { + // Wrap the body in a noop ReadCloser if non-nil. This prevents the + // reader from being closed by the HTTP client. + var rcBody io.ReadCloser + if body != nil { + rcBody = ioutil.NopCloser(body) + } + + // Make the request with the noop-closer for the body. + httpReq, err := http.NewRequest(method, url, rcBody) + if err != nil { + return nil, err + } + + // Check if we can set the Content-Length automatically. + if lr, ok := body.(lenReader); ok { + httpReq.ContentLength = int64(lr.Len()) + } + + return &Request{body, httpReq}, nil +} + +func encode(str string) string { + return base64.StdEncoding.EncodeToString([]byte(str)) +} + +func decode(str string) (string, error) { + bytes, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return "", errors.Errorf("Cannot base64 decode %s", + str) + } + return string(bytes), err +} + +func trimPrefix(s, prefix string) (string, error) { + l := len(s) + trimmed := strings.TrimPrefix(s, prefix) + if l == len(trimmed) { + return trimmed, errors.Errorf("Prefix %s not found in %s", + prefix, s) + } + return trimmed, nil +} + +func drainBody(resp *http.Response) { + defer resp.Body.Close() + io.Copy(ioutil.Discard, resp.Body) +} + +// DoScramSha performs SCRAM-SHA handshake via Http +func DoScramSha(req *Request, + username string, + password string, + client *http.Client) (*http.Response, error) { + + method := "SCRAM-SHA-512" + s, err := NewScramSha("SCRAM-SHA512") + if err != nil { + return nil, errors.Wrap(err, + "Unable to initialize SCRAM-SHA handler") + } + + message, err := s.GetStartRequest(username) + if err != nil { + return nil, err + } + + encodedMessage := method + " " + DataPrefix + encode(message) + + req.Header.Set(Authorization, encodedMessage) + + res, err := client.Do(req.Request) + if err != nil { + return nil, errors.Wrap(err, "Problem sending SCRAM-SHA start"+ + "request") + } + + if res.StatusCode != http.StatusUnauthorized { + return res, nil + } + + authHeader := res.Header.Get(WWWAuthenticate) + if authHeader == "" { + drainBody(res) + return nil, errors.Errorf("Header %s is not populated in "+ + "SCRAM-SHA start response", WWWAuthenticate) + } + + authHeader, err = trimPrefix(authHeader, method+" ") + if err != nil { + if strings.HasPrefix(authHeader, "Basic ") { + // user not found + return res, nil + } + drainBody(res) + return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+ + "start response %s", authHeader) + } + + drainBody(res) + + sid, response, err := parseSidAndData(authHeader) + if err != nil { + return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+ + "start response %s", authHeader) + } + + err = s.HandleStartResponse(response) + if err != nil { + return nil, errors.Wrapf(err, "Error parsing SCRAM-SHA start "+ + "response %s", response) + } + + message = s.GetFinalRequest(password) + encodedMessage = method + " " + SidPrefix + sid + "," + DataPrefix + + encode(message) + + req.Header.Set(Authorization, encodedMessage) + + // rewind request body so it can be resent again + if req.body != nil { + if _, err = req.body.Seek(0, 0); err != nil { + return nil, errors.Errorf("Failed to seek body: %v", + err) + } + } + + res, err = client.Do(req.Request) + if err != nil { + return nil, errors.Wrap(err, "Problem sending SCRAM-SHA final"+ + "request") + } + + if res.StatusCode == http.StatusUnauthorized { + // TODO retrieve and return error + return res, nil + } + + if res.StatusCode >= http.StatusInternalServerError { + // in this case we cannot expect server to set headers properly + return res, nil + } + + authHeader = res.Header.Get(AuthenticationInfo) + if authHeader == "" { + drainBody(res) + return nil, errors.Errorf("Header %s is not populated in "+ + "SCRAM-SHA final response", AuthenticationInfo) + } + + finalSid, response, err := parseSidAndData(authHeader) + if err != nil { + drainBody(res) + return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+ + "final response %s", authHeader) + } + + if finalSid != sid { + drainBody(res) + return nil, errors.Errorf("Sid %s returned by server "+ + "doesn't match the original sid %s", finalSid, sid) + } + + err = s.HandleFinalResponse(response) + if err != nil { + drainBody(res) + return nil, errors.Wrapf(err, + "Error handling SCRAM-SHA final server response %s", + response) + } + return res, nil +} + +func parseSidAndData(authHeader string) (string, string, error) { + sidIndex := strings.Index(authHeader, SidPrefix) + if sidIndex < 0 { + return "", "", errors.Errorf("Cannot find %s in %s", + SidPrefix, authHeader) + } + + sidEndIndex := strings.Index(authHeader, ",") + if sidEndIndex < 0 { + return "", "", errors.Errorf("Cannot find ',' in %s", + authHeader) + } + + sid := authHeader[sidIndex+len(SidPrefix) : sidEndIndex] + + dataIndex := strings.Index(authHeader, DataPrefix) + if dataIndex < 0 { + return "", "", errors.Errorf("Cannot find %s in %s", + DataPrefix, authHeader) + } + + data, err := decode(authHeader[dataIndex+len(DataPrefix):]) + if err != nil { + return "", "", err + } + return sid, data, nil +} |