Skip to content

Commit

Permalink
adding back some missing files
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Feb 28, 2016
1 parent 91bcbc2 commit 71bc54d
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 0 deletions.
5 changes: 5 additions & 0 deletions build-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

go install -ldflags "-X main.buildstamp=`date -u '+%Y%m%dT%H:%M:%SZ'`" ./server
go install ./tinode-db
go install ./keygen
110 changes: 110 additions & 0 deletions server/api_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/******************************************************************************
*
* Copyright (C) 2014 Tinode, All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
* This code is available under licenses for commercial use.
*
* File : auth.go
* Author : Gene Sokolov
* Created : 18-May-2014
*
******************************************************************************
*
* Description :
*
* Authentication
*
*****************************************************************************/

package main

import (
"bytes"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"log"
)

// 32 random bytes to be used for signing auth tokens
// FIXME(gene): move it to the database (make it unique per-application)
var hmac_salt = []byte{
0x4f, 0xbd, 0x77, 0xfe, 0xb6, 0x18, 0x81, 0x6e,
0xe0, 0xe2, 0x6d, 0xef, 0x1b, 0xac, 0xc6, 0x46,
0x1e, 0xfe, 0x14, 0xcd, 0x6d, 0xd1, 0x3f, 0x23,
0xd7, 0x79, 0x28, 0x5d, 0x27, 0x0e, 0x02, 0x3e}

// TODO(gene):change to use snowflake
// getRandomString generates 72 bits of randomness, returns 12 char-long random-looking string
func getRandomString() string {
buf := make([]byte, 9)
_, err := rand.Read(buf)
if err != nil {
panic("getRandomString: failed to generate a random string: " + err.Error())
}
//return base32.StdEncoding.EncodeToString(buf)
return base64.URLEncoding.EncodeToString(buf)
}

// Singned AppID. Composition:
// [1:algorithm version][4:appid][2:key sequence][1:isRoot][16:signature] = 24 bytes
// convertible to base64 without padding
// All integers are little-endian

const (
APIKEY_VERSION = 1
// APIKEY_APPID is deprecated and will be removed in the future
APIKEY_APPID = 4
APIKEY_SEQUENCE = 2
APIKEY_WHO = 1
APIKEY_SIGNATURE = 16
APIKEY_LENGTH = APIKEY_VERSION + APIKEY_APPID + APIKEY_SEQUENCE + APIKEY_WHO + APIKEY_SIGNATURE
)

// Client signature validation
// key: client's secret key
// Returns application id, key type
func checkApiKey(apikey string) (isValid, isRoot bool) {

if declen := base64.URLEncoding.DecodedLen(len(apikey)); declen != APIKEY_LENGTH {
return
}

data, err := base64.URLEncoding.DecodeString(apikey)
if err != nil {
log.Println("failed to decode.base64 appid ", err)
return
}
if data[0] != 1 {
log.Println("unknown appid signature algorithm ", data[0])
return
}

hasher := hmac.New(md5.New, hmac_salt)
hasher.Write(data[:APIKEY_VERSION+APIKEY_APPID+APIKEY_SEQUENCE+APIKEY_WHO])
check := hasher.Sum(nil)
if !bytes.Equal(data[APIKEY_VERSION+APIKEY_APPID+APIKEY_SEQUENCE+APIKEY_WHO:], check) {
log.Println("invalid apikey signature")
return
}

isRoot = (data[APIKEY_VERSION+APIKEY_APPID+APIKEY_SEQUENCE] == 1)

isValid = true

return
}
36 changes: 36 additions & 0 deletions server/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package auth

import (
"github.com/tinode/chat/server/store/types"
)

const (
// No error
NoErr = iota
// DB or other internal failure
ErrInternal
// The secret cannot be parsed or otherwise wrong
ErrMalformed
// Authentication failed (wrong password)
ErrFailed
// Duplicate credential
ErrDuplicate
)

// Interface which auth providers must implement
type AuthHandler interface {
// Add persistent record to database. Returns an error and a bool
// to indicate if the error is due to a duplicate (true) or some other error.
// store.AddAuthRecord("scheme", "unique", "secret")
AddRecord(uid types.Uid, secret string) (int, error)

// Given a user-provided authentication secret (such as "login:password"
// return user ID or error
// store.Users.GetAuthRecord("scheme", "unique")
Authenticate(secret string) (types.Uid, int, error)

// Verify if the provided secret can be considered unique by the auth scheme
// E.g. if login is unique.
// store.GetAuthRecord(scheme, unique)
IsUnique(secret string) (bool, error)
}
93 changes: 93 additions & 0 deletions server/auth_basic/auth_basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package auth_basic

import (
"errors"
"log"
"strings"

"github.com/tinode/chat/server/auth"
"github.com/tinode/chat/server/store"
"github.com/tinode/chat/server/store/types"

"golang.org/x/crypto/bcrypt"
)

type BasicAuth struct{}

func parseSecret(secret string) (uname, password string, err int) {
splitAt := strings.Index(secret, ":")
if splitAt < 1 {
err = auth.ErrMalformed
return
}

err = auth.NoErr
uname = strings.ToLower(secret[:splitAt])
password = secret[splitAt+1:]

return
}

func (BasicAuth) AddRecord(uid types.Uid, secret string) (int, error) {
uname, password, fail := parseSecret(secret)
if fail != auth.NoErr {
return fail, errors.New("basic auth handler: malformed secret")
}

passhash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return auth.ErrInternal, err
}
err, dup := store.Users.AddAuthRecord(uid, "basic", uname, passhash)
if dup {
return auth.ErrDuplicate, err
} else if err != nil {
return auth.ErrInternal, err
}
return auth.NoErr, nil
}

func (BasicAuth) Authenticate(secret string) (types.Uid, int, error) {
uname, password, fail := parseSecret(secret)
if fail != auth.NoErr {
return types.ZeroUid, fail, nil
}

uid, passhash, err := store.Users.GetAuthRecord("basic", uname)
if err != nil {
log.Println(err)
return types.ZeroUid, auth.ErrInternal, err
} else if uid.IsZero() {
// Invalid login.
return types.ZeroUid, auth.ErrFailed, nil
}

err = bcrypt.CompareHashAndPassword([]byte(passhash), []byte(password))
if err != nil {
log.Println(err)
// Invalid password
return types.ZeroUid, auth.ErrFailed, nil
}

return uid, auth.NoErr, nil

}

func (BasicAuth) IsUnique(secret string) (bool, error) {
uname, _, fail := parseSecret(secret)
if fail != auth.NoErr {
return false, errors.New("auth_basic.IsUnique: malformed secret")
}

uid, _, err := store.Users.GetAuthRecord("basic", uname)
if err != nil {
return false, err
}

return !uid.IsZero(), nil
}

func init() {
var auth BasicAuth
store.RegisterAuthScheme("basic", auth)
}

0 comments on commit 71bc54d

Please sign in to comment.