Commit b5ea65b9 authored by Leonard Techel's avatar Leonard Techel
Browse files

Add passwort reset functionality

You can now request a password token (which is stored in the database)
with which the password can be (re-)setted within the webinterface. This
is not really LDAP-aware, but seriously more secure than sending out a
new password via mail.

TODO implement actually sending the token URL via mail. Currently it is
     just printed out on the cmd …

+ Fix .gitignore to seriously ignore the sqlite3 database
parent 5b69607f
conf/app.json conf/app.json
conf/database conf/database.db
...@@ -48,6 +48,15 @@ ...@@ -48,6 +48,15 @@
"Comment": "go1.0-cutoff-74-g8ad2b29", "Comment": "go1.0-cutoff-74-g8ad2b29",
"Rev": "8ad2b298cadd691a77015666a5372eae5dbfac8f" "Rev": "8ad2b298cadd691a77015666a5372eae5dbfac8f"
}, },
{
"ImportPath": "github.com/mattn/go-sqlite3",
"Comment": "v1.1.0-30-g5510da3",
"Rev": "5510da399572b4962c020184bb291120c0a412e2"
},
{
"ImportPath": "github.com/twinj/uuid",
"Rev": "89173bcdda19db0eb88aef1e1cb1cb2505561d31"
},
{ {
"ImportPath": "gopkg.in/asn1-ber.v1", "ImportPath": "gopkg.in/asn1-ber.v1",
"Comment": "v1.1", "Comment": "v1.1",
......
language: go
go:
- 1.4
- 1.3
- 1.2
- tip
notifications:
email: true
\ No newline at end of file
Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
Copyright (C) 2014 by Daniel Kemp <twinj@github.com> Derivative work
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, 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 the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 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.
\ No newline at end of file
Go UUID implementation
========================
[![Build Status](https://travis-ci.org/twinj/uuid.png?branch=master)](https://travis-ci.org/twinj/uuid)
[![GoDoc](http://godoc.org/github.com/twinj/uuid?status.png)](http://godoc.org/github.com/twinj/uuid)
This package provides RFC 4122 compliant UUIDs.
It will generate the following:
* Version 1: based on timestamp and MAC address
* Version 3: based on MD5 hash
* Version 4: based on cryptographically secure random numbers
* Version 5: based on SHA-1 hash
Functions NewV1, NewV3, NewV4, NewV5, New, NewHex and Parse() for generating versions 3, 4
and 5 UUIDs are as specified in [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt).
# Requirements
Go 1.4, 1.3, 1.2 and tip supported.
# Recent Changes
* Removed use of OS Thread locking and runtime package requirement
* Changed String() output to CleanHyphen to match the canonical standard
* Plenty of minor change and housekeeping
* Removed default saver and replaced with interface
* API changes to simplify use.
* Added formatting support for user defined formats
* Added support for Google App Engine
* Variant type bits are now set correctly
* Variant type can now be retrieved more efficiently
* New tests for variant setting to confirm correctness
* New tests added to confirm proper version setting
* Type UUID change to UUIDArray for V3-5 UUIDs and UUIDStruct added for V1 UUIDs
** These implement the BinaryMarshaller and BinaryUnmarshaller interfaces
* New was added to create a base UUID from a []byte slice - this uses UUIDArray
* ParseHex was renamed to ParseUUID
* NewHex now performs unsafe creation of UUID from a hex string
* NewV3 and NewV5 now take anything that implements the Stringer interface
* V1 UUIDs can now be created
* The printing format can be changed
## Installation
Use the `go` tool:
$ go get github.com/twinj/uuid
## Usage
See [documentation and examples](http://godoc.org/github.com/twinj/uuid)
for more information.
var config = uuid.StateSaverConfig{SaveReport: true, SaveSchedule: 30 * time.Minute}
uuid.SetupFileSystemStateSaver(config)
u1 := uuid.NewV1()
uP, _ := uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
u3 := uuid.NewV3(uP, uuid.Name("test"))
u4 := uuid.NewV4()
fmt.Printf(print, u4.Version(), u4.Variant(), u4)
u5 := uuid.NewV5(uuid.NamespaceURL, uuid.Name("test"))
if uuid.Equal(u1, u3) {
fmt.Printf("Will never happen")
}
fmt.Printf(uuid.Formatter(u5, uuid.CurlyHyphen))
uuid.SwitchFormat(uuid.BracketHyphen)
## Copyright
This is a derivative work
Orginal version from
Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>.
See [COPYING](https://github.com/nu7hatch/gouuid/tree/master/COPYING)
file for details.
Also see: Algorithm details in [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt).
Copyright (C) 2014 twinj@github.com
See [LICENSE](https://github.com/twinj/uuid/tree/master/LICENSE)
file for details.
package uuid
/****************
* Date: 1/02/14
* Time: 10:08 AM
***************/
const (
variantIndex = 8
versionIndex = 6
)
// A clean UUID type for simpler UUID versions
type Array [length]byte
func (Array) Size() int {
return length
}
func (o Array) Version() int {
return int(o[versionIndex]) >> 4
}
func (o *Array) setVersion(pVersion int) {
o[versionIndex] &= 0x0F
o[versionIndex] |= byte(pVersion) << 4
}
func (o *Array) Variant() byte {
return variant(o[variantIndex])
}
func (o *Array) setVariant(pVariant byte) {
setVariant(&o[variantIndex], pVariant)
}
func (o *Array) Unmarshal(pData []byte) {
copy(o[:], pData)
}
func (o *Array) Bytes() []byte {
return o[:]
}
func (o Array) String() string {
return formatter(&o, format)
}
func (o Array) Format(pFormat string) string {
return formatter(&o, pFormat)
}
// Set the three most significant bits (bits 0, 1 and 2) of the
// sequenceHiAndVariant equivalent in the array to ReservedRFC4122.
func (o *Array) setRFC4122Variant() {
o[variantIndex] &= 0x3F
o[variantIndex] |= ReservedRFC4122
}
// Marshals the UUID bytes into a slice
func (o *Array) MarshalBinary() ([]byte, error) {
return o.Bytes(), nil
}
// Un-marshals the data bytes into the UUID.
func (o *Array) UnmarshalBinary(pData []byte) error {
return UnmarshalBinary(o, pData)
}
package uuid
/***************
* Date: 14/02/14
* Time: 7:44 PM
***************/
import (
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"encoding/binary"
"log"
seed "math/rand"
"net"
)
const (
length = 16
// 3F used by RFC4122 although 1F works for all
variantSet = 0x3F
// rather than using 0xC0 we use 0xE0 to retrieve the variant
// The result is the same for all other variants
// 0x80 and 0xA0 are used to identify RFC4122 compliance
variantGet = 0xE0
)
var (
// nodeID is the default Namespace node
nodeId = []byte{
// 00.192.79.212.48.200
0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
// The following standard UUIDs are for use with V3 or V5 UUIDs.
NamespaceDNS = &Struct{0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceURL = &Struct{0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceOID = &Struct{0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceX500 = &Struct{0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
state State
)
func init() {
seed.Seed((int64(timestamp())^int64(gregorianToUNIXOffset))*0x6ba7b814<<0x6ba7b812 | 1391463463)
state = State{
randomNode: true,
randomSequence: true,
past: Timestamp((1391463463 * 10000000) + (100 * 10) + gregorianToUNIXOffset),
node: nodeId,
sequence: uint16(seed.Int()) & 0x3FFF,
saver: nil,
}
}
// NewV1 will generate a new RFC4122 version 1 UUID
func NewV1() UUID {
state.Lock()
defer state.Unlock()
now := currentUUIDTimestamp()
state.read(now, currentUUIDNodeId())
state.persist()
return formatV1(now, uint16(1), ReservedRFC4122, state.node)
}
// NewV3 will generate a new RFC4122 version 3 UUID
// V3 is based on the MD5 hash of a namespace identifier UUID and
// any type which implements the UniqueName interface for the name.
// For strings and slices cast to a Name type
func NewV3(pNs UUID, pName UniqueName) UUID {
o := new(Array)
// Set all bits to MD5 hash generated from namespace and name.
Digest(o, pNs, pName, md5.New())
o.setRFC4122Variant()
o.setVersion(3)
return o
}
// NewV4 will generate a new RFC4122 version 4 UUID
// A cryptographically secure random UUID.
func NewV4() UUID {
o := new(Array)
// Read random values (or pseudo-randomly) into Array type.
_, err := rand.Read(o[:length])
if err != nil {
panic(err)
}
o.setRFC4122Variant()
o.setVersion(4)
return o
}
// NewV5 will generate a new RFC4122 version 5 UUID
// Generate a UUID based on the SHA-1 hash of a namespace
// identifier and a name.
func NewV5(pNs UUID, pName UniqueName) UUID {
o := new(Array)
Digest(o, pNs, pName, sha1.New())
o.setRFC4122Variant()
o.setVersion(5)
return o
}
// either generates a random node when there is an error or gets
// the pre initialised one
func currentUUIDNodeId() (node net.HardwareAddr) {
if state.randomNode {
b := make([]byte, 16+6)
_, err := rand.Read(b)
if err != nil {
log.Println("UUID.currentUUIDNodeId error:", err)
node = nodeId
return
}
h := sha1.New()
h.Write(b)
binary.Write(h, binary.LittleEndian, state.sequence)
node = h.Sum(nil)[:6]
if err != nil {
log.Println("UUID.currentUUIDNodeId error:", err)
node = nodeId
return
}
// Mark as randomly generated
node[0] |= 0x01
} else {
node = state.node
}
return
}
// Unmarshal data into struct for V1 UUIDs
func formatV1(pNow Timestamp, pVersion uint16, pVariant byte, pNode []byte) UUID {
o := new(Struct)
o.timeLow = uint32(pNow & 0xFFFFFFFF)
o.timeMid = uint16((pNow >> 32) & 0xFFFF)
o.timeHiAndVersion = uint16((pNow >> 48) & 0x0FFF)
o.timeHiAndVersion |= uint16(pVersion << 12)
o.sequenceLow = byte(state.sequence & 0xFF)
o.sequenceHiAndVariant = byte((state.sequence & 0x3F00) >> 8)
o.sequenceHiAndVariant |= pVariant
o.node = pNode
o.size = length
return o
}
package uuid
/****************
* Date: 21/06/15
* Time: 5:48 PM
***************/
import (
"encoding/gob"
"log"
"os"
"time"
)
func init() {
gob.Register(stateEntity{})
}
func SetupFileSystemStateSaver(pConfig StateSaverConfig) {
saver := &FileSystemSaver{}
saver.saveReport = pConfig.SaveReport
saver.saveSchedule = int64(pConfig.SaveSchedule)
SetupCustomStateSaver(saver)
}
// A wrapper for default setup of the FileSystemStateSaver
type StateSaverConfig struct {
// Print save log
SaveReport bool
// Save every x nanoseconds
SaveSchedule time.Duration
}
// *********************************************** StateEntity
// StateEntity acts as a marshaller struct for the state
type stateEntity struct {
Past Timestamp
Node []byte
Sequence uint16
}
// This implements the StateSaver interface for UUIDs
type FileSystemSaver struct {
cache *os.File
saveState uint64
saveReport bool
saveSchedule int64
}
// Saves the current state of the generator
// If the scheduled file save is reached then the file is synced
func (o *FileSystemSaver) Save(pState *State) {
if pState.past >= pState.next {
err := o.open()
defer o.cache.Close()
if err != nil {
log.Println("uuid.State.save:", err)
return
}
// do the save
o.encode(pState)
// a tick is 100 nano seconds
pState.next = pState.past + Timestamp(o.saveSchedule / 100)
if o.saveReport {
log.Printf("UUID STATE: SAVED %d", pState.past)
}
}
}
func (o *FileSystemSaver) Init(pState *State) {
pState.saver = o
err := o.open()
defer o.cache.Close()
if err != nil {
if os.IsNotExist(err) {
log.Printf("'%s' created\n", "uuid.SaveState")
var err error
o.cache, err = os.Create(os.TempDir() + "/state.unique")
if err != nil {
log.Println("uuid.State.init: SaveState error:", err)
goto pastInit
}
o.encode(pState)
} else {
log.Println("uuid.State.init: SaveState error:", err)
goto pastInit
}
}
err = o.decode(pState)
if err != nil {
goto pastInit
}
pState.randomSequence = false
pastInit:
if timestamp() <= pState.past {
pState.sequence++
}
pState.next = pState.past
}
func (o *FileSystemSaver) reset() {
o.cache.Seek(0, 0)
}
func (o *FileSystemSaver) open() error {
var err error
o.cache, err = os.OpenFile(os.TempDir()+"/state.unique", os.O_RDWR, os.ModeExclusive)
return err
}
// Encodes State generator data into a saved file
func (o *FileSystemSaver) encode(pState *State) {
// ensure reader state is ready for use
o.reset()
enc := gob.NewEncoder(o.cache)
// Wrap private State data into the StateEntity
err := enc.Encode(&stateEntity{pState.past, pState.node, pState.sequence})
if err != nil {
log.Panic("UUID.encode error:", err)
}
}
// Decodes StateEntity data into the main State
func (o *FileSystemSaver) decode(pState *State) error {
o.reset()
dec := gob.NewDecoder(o.cache)
entity := stateEntity{}
err := dec.Decode(&entity)
if err != nil {
log.Println("uuid.decode error:", err)
return err
}
pState.past = entity.Past
pState.node = entity.Node
pState.sequence = entity.Sequence
return nil
}
package uuid
/****************
* Date: 14/02/14
* Time: 7:43 PM
***************/
import (
"bytes"
"log"
seed "math/rand"
"net"
"sync"
)
// **************************************************** State
func SetupCustomStateSaver(pSaver StateSaver) {
state.Lock()
pSaver.Init(&state)
state.init()
state.Unlock()
}
// Holds package information about the current
// state of the UUID generator
type State struct {
// A flag which informs whether to
// randomly create a node id
randomNode bool
// A flag which informs whether to
// randomly create the sequence
randomSequence bool
// the last time UUID was saved
past Timestamp
// the next time the state will be saved
next Timestamp
// the last node which saved a UUID
node []byte
// An iterated value to help ensure different
// values across the same domain
sequence uint16
sync.Mutex
// save state interface
saver StateSaver
}
// Changes the state with current data
// Compares the current found node to the last node stored,
// If they are the same or randomSequence is already set due
// to an earlier read issue then the sequence is randomly generated
// else if there is an issue with the time the sequence is incremented
func (o *State) read(pNow Timestamp, pNode net.HardwareAddr) {
if bytes.Equal([]byte(pNode), o.node) || o.randomSequence {
o.sequence = uint16(seed.Int()) & 0x3FFF
} else if pNow < o.past {
o.sequence++
}
o.past = pNow
o.node = pNode
}
func (o *State) persist() {
if o.saver != nil {
o.saver.Save(o)
}
}
// Initialises the UUID state when the package is first loaded
// it first attempts to decode the file state into State
// if this file does not exist it will create the file and do a flush
// of the random state which gets loaded at package runtime
// second it will attempt to resolve the current hardware address nodeId
// thirdly it will check the state of the clock
func (o *State) init() {
if o.saver != nil {
intfcs, err := net.Interfaces()
if err != nil {
log.Println("uuid.State.init: address error: will generate random node id instead", err)
return
}
a := getHardwareAddress(intfcs)
if a == nil {
log.Println("uuid.State.init: address error: will generate random node id instead", err)
return
}
// Don't use random as we have a real address
o.randomSequence = false
if bytes.Equal([]byte(a), state.node) {
state.sequence++
}
state.node = a
state.randomNode = false
}
}