// Create a new *UserState that can be used for managing users. // dbindex is the Redis database index (0 is a good default value). // If randomseed is true, the random number generator will be seeded after generating the cookie secret (true is a good default value). // redisHostPort is host:port for the desired Redis server (can be blank for localhost) // Also creates a new ConnectionPool. func NewUserState(dbindex int, randomseed bool, redisHostPort string) *UserState { var pool *simpleredis.ConnectionPool // Connnect to the default redis server if redisHostPort is empty if redisHostPort == "" { redisHostPort = defaultRedisServer } // Test connection if err := simpleredis.TestConnectionHost(redisHostPort); err != nil { log.Fatalln(err.Error()) } // Aquire connection pool pool = simpleredis.NewConnectionPoolHost(redisHostPort) state := new(UserState) state.users = simpleredis.NewHashMap(pool, "users") state.users.SelectDatabase(dbindex) state.usernames = simpleredis.NewSet(pool, "usernames") state.usernames.SelectDatabase(dbindex) state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed") state.unconfirmed.SelectDatabase(dbindex) state.pool = pool state.dbindex = dbindex // For the secure cookies // This must happen before the random seeding, or // else people will have to log in again after every server restart state.cookieSecret = cookie.RandomCookieFriendlyString(30) // Seed the random number generator if randomseed { rand.Seed(time.Now().UnixNano()) } // Cookies lasts for 24 hours by default. Specified in seconds. state.cookieTime = cookie.DefaultCookieTime // Default password hashing algorithm is "bcrypt+", which is the same as // "bcrypt", but with backwards compatibility for checking sha256 hashes. state.passwordAlgorithm = "bcrypt+" // "bcrypt+", "bcrypt" or "sha256" if pool.Ping() != nil { defer pool.Close() log.Fatalf("Error, wrong hostname, port or password. (%s does not reply to PING)\n", redisHostPort) } return state }
// Use one of the databases for the permission middleware, // assign a name to dbName (used for the status output) and // return a Permissions struct. func mustAquirePermissions() pinterface.IPermissions { var ( err error perm pinterface.IPermissions ) // If Bolt is to be used and no filename is given if useBolt && (boltFilename == "") { boltFilename = defaultBoltFilename } if boltFilename != "" { // New permissions middleware, using a Bolt database perm, err = bolt.NewWithConf(boltFilename) if err != nil { if err.Error() == "timeout" { tempFile, err := ioutil.TempFile("", "algernon") if err != nil { log.Fatal("Unable to find a temporary file to use:", err) } else { boltFilename = tempFile.Name() + ".db" } } else { log.Errorf("Could not use Bolt as database backend: %s", err) } } else { dbName = "Bolt (" + boltFilename + ")" } // Try the new database filename if there was a timeout if dbName == "" && boltFilename != defaultBoltFilename { perm, err = bolt.NewWithConf(boltFilename) if err != nil { if err.Error() == "timeout" { log.Error("The Bolt database timed out!") } else { log.Errorf("Could not use Bolt as database backend: %s", err) } } else { dbName = "Bolt, temporary" } } } if dbName == "" && mariadbDSN != "" { // New permissions middleware, using a MariaDB/MySQL database perm, err = mariadb.NewWithDSN(mariadbDSN, mariaDatabase) if err != nil { log.Errorf("Could not use MariaDB/MySQL as database backend: %s", err) } else { // The connection string may contain a password, so don't include it in the dbName dbName = "MariaDB/MySQL" } } if dbName == "" && mariaDatabase != "" { // Given a database, but not a host, connect to localhost // New permissions middleware, using a MariaDB/MySQL database perm, err = mariadb.NewWithConf("test:@127.0.0.1/" + mariaDatabase) if err != nil { if mariaDatabase != "" { log.Errorf("Could not use MariaDB/MySQL as database backend: %s", err) } else { log.Warnf("Could not use MariaDB/MySQL as database backend: %s", err) } } else { // The connection string may contain a password, so don't include it in the dbName dbName = "MariaDB/MySQL" } } if dbName == "" { // New permissions middleware, using a Redis database if err := simpleredis.TestConnectionHost(redisAddr); err != nil { // Only output an error when a Redis host other than the default host+port was specified if redisAddrSpecified { if singleFileMode { log.Warnf("Could not use Redis as database backend: %s", err) } else { log.Errorf("Could not use Redis as database backend: %s", err) } } } else { perm = redis.NewWithRedisConf(redisDBindex, redisAddr) dbName = "Redis" } } if dbName == "" && boltFilename == "" { boltFilename = defaultBoltFilename perm, err = bolt.NewWithConf(boltFilename) if err != nil { if err.Error() == "timeout" { tempFile, err := ioutil.TempFile("", "algernon") if err != nil { log.Fatal("Unable to find a temporary file to use:", err) } else { boltFilename = tempFile.Name() + ".db" } } else { log.Errorf("Could not use Bolt as database backend: %s", err) } } else { dbName = "Bolt (" + boltFilename + ")" } // Try the new database filename if there was a timeout if boltFilename != defaultBoltFilename { perm, err = bolt.NewWithConf(boltFilename) if err != nil { if err.Error() == "timeout" { log.Error("The Bolt database timed out!") } else { log.Errorf("Could not use Bolt as database backend: %s", err) } } else { dbName = "Bolt, temporary" } } } if dbName == "" { // This may typically happen if Algernon is already running log.Fatalln("Could not find a usable database backend.") } return perm }