예제 #1
0
func (db *dbClient) newRedisClient() (rdisClient, error) {
	if db.config.DatabaseAddress == "" {
		return nil, errMissingDatabaseHost
	}

	if db.config.DatabasePass == "" {
		logger.Info("Insecure redis database is not recommended")
	}

	r := &rClient{}
	r.Client = redis.NewClient(&redis.Options{
		//r.Client = redis.NewClient(&redis.Options{
		Addr:     db.config.DatabaseAddress,
		Password: db.config.DatabasePass,
		DB:       db.config.DatabaseUser,
	})

	// Confirm we can communicate with the redis instance.
	_, err := r.Ping().Result()
	if err != nil {
		return nil, errors.Wrap(err, "r.Pint().Result")
	}

	return r, nil
}
예제 #2
0
func main() {
	http.HandleFunc("/", http.NotFound)
	http.HandleFunc("/ws", newConnection)
	logger.Info("Starting Tiberious on", config.Port)
	if err := http.ListenAndServe(config.Port, nil); err != nil {
		logger.Error(err)
	}
}
예제 #3
0
// WriteGroupData writes a given group object to the current database.
func (db *dbClient) WriteGroupData(group *types.Group) error {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.writeGroupData(group)
	default:
		// TODO determine if this should be a fatal error, it should never happen
		logger.Info("Invalid config, no group data written")
		break
	}

	return nil
}
예제 #4
0
// NewDB returns a new database client
func NewDB(c settings.Config) (Client, error) {
	client := &dbClient{
		config: c,
	}

	if client.config.UserDatabase == 0 {
		// Load Redis DB
		var err error
		client.rdis, err = client.newRedisClient()
		if err != nil {
			return client, errors.Wrap(err, "newRedisClient")
		}

		logger.Info("User database started on redis db", client.config.DatabaseUser)
	}

	return client, nil
}
예제 #5
0
package db

import (
	"strings"
	"tiberious/logger"
	"tiberious/settings"
	"tiberious/types"

	"github.com/pkg/errors"
)

type (
	// Client provides access to the database
	Client interface {
		GetKeySet(search string) ([]string, error)
		WriteUserData(user *types.User) error
		WriteRoomData(room *types.Room) error
		WriteGroupData(group *types.Group) error
		UserExists(id string) (bool, error)
		RoomExists(gname, rname string) (bool, error)
		GroupExists(gname string) (bool, error)
		GetUserData(id string) (*types.User, error)
		GetRoomData(gname, rname string) (*types.Room, error)
		GetGroupData(gname string) (*types.Group, error)
		DeleteUser(user *types.User) error
	}

	dbClient struct {
		config settings.Config

		rdis rdisClient
	}
)

// NewDB returns a new database client
func NewDB(c settings.Config) (Client, error) {
	client := &dbClient{
		config: c,
	}

	if client.config.UserDatabase == 0 {
		// Load Redis DB
		var err error
		client.rdis, err = client.newRedisClient()
		if err != nil {
			return client, errors.Wrap(err, "newRedisClient")
		}

		logger.Info("User database started on redis db", client.config.DatabaseUser)
	}

	return client, nil
}

// GetKeySet returns all the keys that match a given search pattern.
func (db *dbClient) GetKeySet(search string) ([]string, error) {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.getKeySet(search)
	default:
		break
	}

	return nil, types.NotInDB
}

// WriteUserData writes a given user object to the current database.
func (db *dbClient) WriteUserData(user *types.User) error {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.writeUserData(user)
	default:
		// TODO determine if this should be a fatal error, it should never happen
		logger.Info("Invalid config, no user data written")
		break
	}

	return nil
}

// WriteRoomData writes a given room object to the current database.
func (db *dbClient) WriteRoomData(room *types.Room) error {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.writeRoomData(room)
	default:
		// TODO determine if this should be a fatal error, it should never happen
		logger.Info("Invalid config, no room data written")
		break
	}

	return nil
}

// WriteGroupData writes a given group object to the current database.
func (db *dbClient) WriteGroupData(group *types.Group) error {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.writeGroupData(group)
	default:
		// TODO determine if this should be a fatal error, it should never happen
		logger.Info("Invalid config, no group data written")
		break
	}

	return nil
}

// UserExists returns whether a user exists in the database.
func (db *dbClient) UserExists(id string) (bool, error) {
	res, err := db.GetKeySet("user-*-*-" + id)
	if err != nil {
		return false, errors.Wrap(err, "GetKeySet")
	}

	if len(res) == 0 {
		return false, nil
	}

	return true, nil
}

// RoomExists returns whether a room exists in the database.
func (db *dbClient) RoomExists(gname, rname string) (bool, error) {
	res, err := db.GetKeySet("room-" + gname + "-" + rname + "*")
	if err != nil {
		return false, errors.Wrap(err, "GetKeySet")
	}

	if len(res) == 0 {
		return false, nil
	}

	return true, nil
}

// GroupExists returns whether a group exists in the database.
func (db *dbClient) GroupExists(gname string) (bool, error) {
	res, err := db.GetKeySet("group-" + gname + "-*")
	if err != nil {
		return false, errors.Wrap(err, "GetKeySet")
	}

	if len(res) == 0 {
		return false, nil
	}

	return true, nil
}

// GetUserData gets all the data for a given user ID from the database
func (db *dbClient) GetUserData(id string) (*types.User, error) {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.getUserData(id)
	default:
		break
	}

	return nil, types.NotInDB
}

// GetRoomData gets all the data for a given room (group required) from the database
func (db *dbClient) GetRoomData(gname, rname string) (*types.Room, error) {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.getRoomData(gname, rname)
	default:
		break
	}

	return nil, types.NotInDB
}

// GetGroupData gets all the data for a given group from the database
func (db *dbClient) GetGroupData(gname string) (*types.Group, error) {
	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.getGroupData(gname)
	default:
		break
	}

	return nil, types.NotInDB
}

// DeleteUser removes a user from all rooms and groups and deletes it from the
// database (use sparingly). */
func (db *dbClient) DeleteUser(user *types.User) error {
	for _, gname := range user.Groups {
		group, err := db.GetGroupData(gname)
		if err != nil {
			return errors.Wrap(err, "GetGroupData")
		}

		delete(group.Users, user.ID.String())
		if err := db.WriteGroupData(group); err != nil {
			return errors.Wrap(err, "WriteGroupData")
		}
	}

	for _, rname := range user.Rooms {
		slice := strings.Split(rname, "/")
		room, err := db.GetRoomData(slice[0], slice[1])
		if err != nil {
			return errors.Wrap(err, "GetRoomData")
		}

		delete(room.Users, user.ID.String())
		if err := db.WriteRoomData(room); err != nil {
			return errors.Wrap(err, "WriteRoomData")
		}
	}

	switch {
	case db.config.UserDatabase == 0:
		return db.rdis.deleteUser(user)
	default:
		// TODO determine if this should be a fatal error, it should never happen
		logger.Info("Invalid config, user not deleted")
		break
	}

	return nil
}
// ClientHandler handles all client interactions
func ClientHandler(conn *websocket.Conn) {
	var err error

	client := types.NewClient()
	client.Conn = conn
	client.User = new(types.User)
	// Set the UUID and initialize a username of "guest"
	client.User.ID, err = getUniqueID()
	if err != nil {
		logger.Error(err)
	}

	guests, err := dbClient.GetKeySet("user-guest-*-*")
	if err != nil {
		logger.Error(err)
	}

	// TODO give guests a numeric suffix, allow disabling guest connections.
	client.User.Username = "******" + strconv.Itoa(len(guests)+1)
	client.User.LoginName = client.User.Username
	client.User.Type = "guest"
	client.User.Connected = true

	clients[client.User.ID.String()] = client

	if config.AllowGuests {
		defgroup, err := GetGroup("#default")
		if err != nil {
			logger.Error(err)
		}
		defgroup.Users[client.User.ID.String()] = client.User
		client.User.Groups = append(client.User.Groups, "#default")
		room, err := GetRoom("#default", "#general")
		if err != nil {
			logger.Error(err)
		}
		client.User.Rooms = append(client.User.Rooms, "#default/#general")
		room.Users[client.User.ID.String()] = client.User

		dbClient.WriteUserData(client.User)
		dbClient.WriteGroupData(defgroup)
		dbClient.WriteRoomData(room)

		logger.Info("guest", client.User.ID.String(), "connected")
	} else {
		logger.Info("new client connected")
	}

	if err := client.Alert(types.OK, ""); err != nil {
		logger.Error(err)
	}

	/* TODO we may want to remove this later it's just for easy testing.
	 * to allow a client to get their UUID back from the server after
	 * connecting. */
	if config.AllowGuests {
		if err := client.Alert(types.GeneralNotice, string("Connected as guest with ID "+client.User.ID.String())); err != nil {
			logger.Error(err)
		}
	} else {
		if err := client.Alert(types.ImportantNotice, "No Guests Allowed : send authentication token to continue"); err != nil {
			logger.Error(err)
		}
	}

	/* Never return from this loop!
	 * Never break from this loop unless intending to disconnect the client. */
	for {
		_, rawmsg, err := client.Conn.ReadMessage()
		if err != nil {
			switch {
			case websocket.IsCloseError(err, websocket.CloseNormalClosure):
				if client.User != nil {
					logger.Info(client.User.Type, client.User.ID.String(), "disconnected")
				} else {
					logger.Info("client disconnected")
				}
				break
			// TODO handle these different cases appropriately.
			case websocket.IsCloseError(err, websocket.CloseGoingAway):
			case websocket.IsCloseError(err, websocket.CloseProtocolError, websocket.CloseUnsupportedData):
				// This should utilize the ban-score to combat possible spammers
			case websocket.IsCloseError(err, websocket.ClosePolicyViolation, websocket.CloseMessageTooBig):
				// These should also utilize the ban-score but with a higher ban
			default:
				logger.Info(err)
			}
			break
		}

		ban, err := ParseMessage(client, rawmsg)
		if err != nil {
			logger.Info(err)
		}
		if ban > 0 {
			// TODO handle ban-score
			break
		}
	}

	// We broke out of the loop so disconnect the client.
	client.Conn.Close()
	if client.User != nil {
		if client.User.Type == "guest" {
			if err := dbClient.DeleteUser(client.User); err != nil {
				logger.Error(err)
			}
		} else {
			client.User.Connected = false
			dbClient.WriteUserData(client.User)
		}
	}

	delete(clients, client.User.ID.String())
}