mirror of
https://github.com/tinode/chat.git
synced 2025-03-14 10:05:07 +00:00
adding back some missing files
This commit is contained in:
5
build-all.sh
Executable file
5
build-all.sh
Executable file
@ -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
server/api_key.go
Normal file
110
server/api_key.go
Normal file
@ -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
server/auth/auth.go
Normal file
36
server/auth/auth.go
Normal file
@ -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
server/auth_basic/auth_basic.go
Normal file
93
server/auth_basic/auth_basic.go
Normal file
@ -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)
|
||||
}
|
Reference in New Issue
Block a user