Skip to content

Commit

Permalink
Merge pull request #7 from labbsr0x/usercredentials
Browse files Browse the repository at this point in the history
Usercredentials
  • Loading branch information
eabili0 authored Apr 25, 2019
2 parents 63f19bc + d532df8 commit e6762ba
Show file tree
Hide file tree
Showing 28 changed files with 777 additions and 272 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# BUILD
FROM abilioesteves/gowebbuilder:v0.2.3 as builder
FROM abilioesteves/gowebbuilder:v0.3.0 as builder

ENV p $GOPATH/src/github.com/abilioesteves/whisper
ENV p $GOPATH/src/github.com/labbsr0x/whisper

ADD ./ ${p}
WORKDIR ${p}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
A Login and Consent provider implementation in Go for Ory Hydra



# Developer

From the project root folder, fire the following commands to execute this project in development mode:
Expand Down
5 changes: 3 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package cmd

import (
"fmt"
"os"
"strings"

"github.com/sirupsen/logrus"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -21,7 +22,7 @@ var rootCmd = &cobra.Command{
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
logrus.Error(err)
os.Exit(1)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var serveCmd = &cobra.Command{
}
}

return nil
return err
},
}

Expand Down
119 changes: 109 additions & 10 deletions db/usercredentials.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,134 @@
package db

import (
"fmt"
"net/url"
"strings"
"time"

"github.com/labbsr0x/whisper/misc"

"github.com/sirupsen/logrus"

"github.com/google/uuid"

"github.com/labbsr0x/goh/gohtypes"

"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)

// UserCredential holds the information from a user credential
type UserCredential struct {
ID string `gorm:"primary_key;not null;"`
Username string `gorm:"unique_index;not null;"`
Email string `gorm:"index"`
Password string `gorm:"not null;"`
Salt string `gorm:"not null;"`
CreatedAt time.Time
UpdatedAt time.Time
}

// UserCredentialsDAO defines the methods that can be performed
type UserCredentialsDAO interface {
CreateUserCredential(username, password, clientID string) (string, error)
UpdateUserCredential(userID uuid.UUID, username, password string) error
DeleteUserCredential(userID uuid.UUID) error
CreateUserCredential(username, password, email string) (string, error)
UpdateUserCredential(username, email, password string) error
GetUserCredential(username string) (UserCredential, error)
InitFromDatabaseURL(dbURL string) UserCredentialsDAO
CheckCredentials(username, password string) (bool, error)
}

// DefaultUserCredentialsDAO a default UserCredentialsDAO interface implementation
type DefaultUserCredentialsDAO struct {
DatabaseURL string
SecretKey string
}

// InitFromDatabaseURL initializes a defualt user credentials DAO from web builder
func (dao *DefaultUserCredentialsDAO) InitFromDatabaseURL(dbURL string) UserCredentialsDAO {
u, err := url.Parse(dbURL)
gohtypes.PanicIfError("Unable to parse db url", 500, err)
dao.DatabaseURL = strings.Replace(u.String(), u.Scheme+"://", "", 1)

gohtypes.PanicIfError("Not possible to migrate db", 500, dao.migrate())

dao.SecretKey = "y6VaBTeP5ROoUcPPAThW"
return dao
}

// migrate initializes a migration routine to synchronize db and model
func (dao *DefaultUserCredentialsDAO) migrate() error {
db, err := gorm.Open("mysql", dao.DatabaseURL)
if err == nil {
defer db.Close()
db.AutoMigrate(&UserCredential{})
}
logrus.Error(err)
return err
}

// CreateUserCredential creates a user
func (dao *DefaultUserCredentialsDAO) CreateUserCredential(username, password, clientID string) (string, error) {
return "", nil
func (dao *DefaultUserCredentialsDAO) CreateUserCredential(username, password, email string) (string, error) {
db, err := gorm.Open("mysql", dao.DatabaseURL)
if err == nil {
defer db.Close()
salt := misc.GenerateSalt()
hPassword := misc.GetEncryptedPassword(dao.SecretKey, password, salt)
userCredential := UserCredential{ID: uuid.New().String(), Username: username, Password: hPassword, Email: email, Salt: salt}
db.NewRecord(userCredential)

db.Create(&userCredential)

if !db.NewRecord(userCredential) {
return userCredential.ID, nil
}

err = fmt.Errorf("Unable to create an user credential: %v", db.GetErrors())
}
return "", err
}

// UpdateUserCredential updates a user
func (dao *DefaultUserCredentialsDAO) UpdateUserCredential(userID uuid.UUID, username, password string) error {
return nil
func (dao *DefaultUserCredentialsDAO) UpdateUserCredential(username, email, password string) error {
db, err := gorm.Open("mysql", dao.DatabaseURL)
if err == nil {
defer db.Close()

salt := misc.GenerateSalt()
hPassword := misc.GetEncryptedPassword(dao.SecretKey, password, salt)

userCredential := UserCredential{}
db.Where("username = ?", username).First(&userCredential)

userCredential.Password = hPassword
userCredential.Salt = salt
userCredential.Email = email

db = db.Save(userCredential)
err = db.Error
}
return err
}

// GetUserCredential gets an user credential
func (dao *DefaultUserCredentialsDAO) GetUserCredential(username string) (UserCredential, error) {
userCredential := UserCredential{}
db, err := gorm.Open("mysql", dao.DatabaseURL)
if err == nil {
defer db.Close()

db = db.Where("username = ?", username).First(&userCredential)
err = db.Error
}
return userCredential, err
}

// DeleteUserCredential deletes the user
func (dao *DefaultUserCredentialsDAO) DeleteUserCredential(userID uuid.UUID) error {
return nil
// CheckCredentials verifies if the informed credentials are valid
func (dao *DefaultUserCredentialsDAO) CheckCredentials(username, password string) (bool, error) {
userCredential, err := dao.GetUserCredential(username)
if err == nil {
hPassword := misc.GetEncryptedPassword(dao.SecretKey, password, userCredential.Salt)
return hPassword == userCredential.Password, nil
}
return false, err
}
35 changes: 14 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
build: .
image: abilioesteves/whisper:0.0.1
depends_on:
- postgresd-whisper
- mysqld
- hydra
ports:
- "7070:7070"
Expand All @@ -18,14 +18,15 @@ services:
- WHISPER_HYDRA_PUBLIC_URL=http://hydra:4444/
- WHISPER_CLIENT_ID=whisper
- WHISPER_CLIENT_SECRET=whisper
- WHISPER_DATABASE_URL=postgres://whisper:secret@postgresd-whisper:5431/whisper?sslmode=disable
- WHISPER_DATABASE_URL=mysql://root:secret@tcp(mysqld:3306)/whisper?charset=utf8mb4&parseTime=True&loc=Local
restart: on-failure

hydra-migrate:
image: oryd/hydra:v1.0.0-rc.8
depends_on:
- postgresd-hydra
- mysqld
environment:
- DATABASE_URL=postgres://hydra:secret@postgresd-hydra:5432/hydra?sslmode=disable
- DSN=mysql://root:secret@tcp(mysqld:3306)/hydra?max_conns=20&max_idle_conns=4
command:
migrate sql -e
restart: on-failure
Expand All @@ -48,31 +49,23 @@ services:
- OAUTH2_ISSUER_URL=http://localhost:4444
- OAUTH2_CONSENT_URL=http://localhost:7070/consent
- OAUTH2_LOGIN_URL=http://localhost:7070/login
- DATABASE_URL=postgres://hydra:secret@postgresd-hydra:5432/hydra?sslmode=disable
- DSN=mysql://root:secret@tcp(mysqld:3306)/hydra?max_conns=20&max_idle_conns=4
- SYSTEM_SECRET=youReallyNeedToChangeThis
- OAUTH2_SHARE_ERROR_DEBUG=1
- OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise
- OIDC_SUBJECT_TYPE_PAIRWISE_SALT=youReallyNeedToChangeThis
- CORS_ENABLED=true
# - OAUTH2_ACCESS_TOKEN_STRATEGY=jwt

postgresd-hydra:
image: postgres:9.6
ports:
- "5432:5432"
environment:
- POSTGRES_USER=hydra
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=hydra
# - OAUTH2_ACCESS_TOKEN_STRATEGY=jwt
restart: on-failure

postgresd-whisper:
image: postgres:9.6
mysqld:
image: mysql:5.7
ports:
- "5431:5432"
- "3306:3306"
volumes:
- ./scripts:/docker-entrypoint-initdb.d
environment:
- POSTGRES_USER=whisper
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=whisper
- MYSQL_ROOT_PASSWORD=secret

prometheus:
image: abilioesteves/prometheus:1.0.0
Expand Down
47 changes: 18 additions & 29 deletions misc/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package misc

import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strings"
"io"
)

// ConvertInterfaceArrayToStringArray a helper method to perform conversions between []interface{} and []string
Expand All @@ -28,32 +30,19 @@ func GetJSONStr(toEncode interface{}) string {
return buf.String()
}

// GetAccessTokenFromRequest is a helper method to recover an Access Token from a http request
func GetAccessTokenFromRequest(r *http.Request) (string, error) {
auth := r.Header.Get("Authorization")
var t string

if len(auth) == 0 {
return "", fmt.Errorf("No Authorization Header found")
}

data := strings.Split(auth, " ")

if len(data) != 2 {
return "", fmt.Errorf("Bad Authorization Header")
}

t = data[0]

if len(t) == 0 || t != "Bearer" {
return "", fmt.Errorf("No Bearer Token found")
}

t = data[1]

if len(t) == 0 {
return "", fmt.Errorf("Bad Authorization Header")
// GenerateSalt a salt string with 16 bytes of crypto/rand data.
func GenerateSalt() string {
randomBytes := make([]byte, 16)
_, err := rand.Read(randomBytes)
if err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(randomBytes)
}

return t, nil
// GetEncryptedPassword builds an encrypted password with hmac(sha256)
func GetEncryptedPassword(secretKey, password, salt string) string {
hash := hmac.New(sha256.New, []byte(secretKey))
io.WriteString(hash, password+salt)
return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}
5 changes: 0 additions & 5 deletions scopes.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,5 @@
"Description": "Always Sign in",
"Scope": "offline",
"Details": "Provides the possibility for the app to be always signed in to your account"
},
"register-user": {
"Description": "Register a new user",
"Scope": "register-user",
"Details": "Provides the possibility for the app to register new Whisper users"
}
}
15 changes: 15 additions & 0 deletions scripts/dbinit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

# Functions
ok() { echo -e '\e[32m'$1'\e[m'; } # Green

MYSQL=`which mysql`

Q1="CREATE DATABASE IF NOT EXISTS hydra;"
Q2="CREATE DATABASE IF NOT EXISTS whisper;"

SQL="${Q1}${Q2}"

$MYSQL -uroot -p$MYSQL_ROOT_PASSWORD -e "${SQL}"

ok "Created databases hydra and whisper"
Loading

0 comments on commit e6762ba

Please sign in to comment.