mirror of
https://github.com/ddworken/hishtory.git
synced 2025-02-02 11:39:24 +01:00
Add support for limiting the number of registrations to fix #46
This commit is contained in:
parent
5ee48f3d5f
commit
9ed325e0a5
@ -110,7 +110,10 @@ But if you'd like to self-host the hishtory backend, you can! The backend is a s
|
|||||||
|
|
||||||
Check out the [`docker-compose.yml`](https://github.com/ddworken/hishtory/blob/master/backend/server/docker-compose.yml) file for an example config to start a hiSHtory server using postgres.
|
Check out the [`docker-compose.yml`](https://github.com/ddworken/hishtory/blob/master/backend/server/docker-compose.yml) file for an example config to start a hiSHtory server using postgres.
|
||||||
|
|
||||||
If you want to use a SQLite backend, you can do so by setting the `HISHTORY_SQLITE_DB` environment variable to point to a file. It will then create a SQLite DB at the given location.
|
A few configuration options:
|
||||||
|
|
||||||
|
* If you want to use a SQLite backend, you can do so by setting the `HISHTORY_SQLITE_DB` environment variable to point to a file. It will then create a SQLite DB at the given location.
|
||||||
|
* If you want to limit the number of users that your server allows (e.g. because you only intend to use the server for yourself), you can set the environment variable `HISHTORY_MAX_NUM_USERS=1` (or to whatever value you wish for the limit to be). Leave it unset to allow registrations with no cap.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"html"
|
"html"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -271,6 +272,17 @@ func getRemoteAddr(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func apiRegisterHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
func apiRegisterHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if getMaximumNumberOfAllowedUsers() < math.MaxInt {
|
||||||
|
row := GLOBAL_DB.WithContext(ctx).Raw("SELECT COUNT(DISTINCT devices.user_id) FROM devices").Row()
|
||||||
|
var numDistinctUsers int64 = 0
|
||||||
|
err := row.Scan(&numDistinctUsers)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if numDistinctUsers >= int64(getMaximumNumberOfAllowedUsers()) {
|
||||||
|
panic(fmt.Sprintf("Refusing to allow registration of new device since there are currently %d users and this server allows a max of %d users", numDistinctUsers, getMaximumNumberOfAllowedUsers()))
|
||||||
|
}
|
||||||
|
}
|
||||||
userId := getRequiredQueryParam(r, "user_id")
|
userId := getRequiredQueryParam(r, "user_id")
|
||||||
deviceId := getRequiredQueryParam(r, "device_id")
|
deviceId := getRequiredQueryParam(r, "device_id")
|
||||||
var existingDevicesCount int64 = -1
|
var existingDevicesCount int64 = -1
|
||||||
@ -852,4 +864,16 @@ func checkGormResult(result *gorm.DB) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMaximumNumberOfAllowedUsers() int {
|
||||||
|
maxNumUsersStr := os.Getenv("HISHTORY_MAX_NUM_USERS")
|
||||||
|
if maxNumUsersStr == "" {
|
||||||
|
return math.MaxInt
|
||||||
|
}
|
||||||
|
maxNumUsers, err := strconv.Atoi(maxNumUsersStr)
|
||||||
|
if err != nil {
|
||||||
|
return math.MaxInt
|
||||||
|
}
|
||||||
|
return maxNumUsers
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(optimization): Maybe optimize the endpoints a bit to reduce the number of round trips required?
|
// TODO(optimization): Maybe optimize the endpoints a bit to reduce the number of round trips required?
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -521,6 +522,27 @@ func TestHealthcheck(t *testing.T) {
|
|||||||
assertNoLeakedConnections(t, GLOBAL_DB)
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLimitRegistrations(t *testing.T) {
|
||||||
|
// Set up
|
||||||
|
InitDB()
|
||||||
|
defer testutils.BackupAndRestoreEnv("HISHTORY_MAX_NUM_USERS")()
|
||||||
|
os.Setenv("HISHTORY_MAX_NUM_USERS", "2")
|
||||||
|
|
||||||
|
// Register three devices across two users
|
||||||
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user1"), nil)
|
||||||
|
apiRegisterHandler(context.Background(), nil, deviceReq)
|
||||||
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user1"), nil)
|
||||||
|
apiRegisterHandler(context.Background(), nil, deviceReq)
|
||||||
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user2"), nil)
|
||||||
|
apiRegisterHandler(context.Background(), nil, deviceReq)
|
||||||
|
|
||||||
|
// And this next one should fail since it is a new user
|
||||||
|
defer func() { _ = recover() }()
|
||||||
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user3"), nil)
|
||||||
|
apiRegisterHandler(context.Background(), nil, deviceReq)
|
||||||
|
t.Errorf("expected panic")
|
||||||
|
}
|
||||||
|
|
||||||
func assertNoLeakedConnections(t *testing.T, db *gorm.DB) {
|
func assertNoLeakedConnections(t *testing.T, db *gorm.DB) {
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user