summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandomChars <random@chars.jp>2021-09-02 12:51:36 +0900
committerRandomChars <random@chars.jp>2021-09-02 12:51:36 +0900
commit24b7a28336b903b7a8fac3588e48e821081929ac (patch)
tree63030ef5fc5525e6a6245a081187f8ac7514d121
parent0dace376a532fc60e5cf4cdcdcb57b8eff79820e (diff)
private modev1.1.0
-rw-r--r--api.go91
-rw-r--r--api/paths.go1
-rw-r--r--client/remote.go19
-rw-r--r--config.go38
-rw-r--r--store/store.go5
5 files changed, 140 insertions, 14 deletions
diff --git a/api.go b/api.go
index 5d78674..74ffd6d 100644
--- a/api.go
+++ b/api.go
@@ -28,11 +28,23 @@ func registerAPI() {
context.JSON(http.StatusOK, instance.SingleUser)
})
+ router.GET(api.Private, func(context *gin.Context) {
+ context.JSON(http.StatusOK, instance.Private)
+ })
+
router.GET(api.User, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.Users())
})
router.PUT(api.User, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
user, ok := getUser(context)
if !instance.Register {
if !ok {
@@ -73,6 +85,10 @@ func registerAPI() {
})
router.GET(api.UserField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
info := instance.User(context.Param("flake"))
context.JSON(http.StatusOK, api.UserPayload{
Username: info.Username,
@@ -176,6 +192,10 @@ func registerAPI() {
})
router.GET(api.UsernameField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
info := instance.UserUsername(context.Param("name"))
payload := api.UserPayload{
Username: info.Username,
@@ -212,6 +232,7 @@ func registerAPI() {
return
}
+ // Only allow lookup if user is current user or privileged
flake := context.Param("flake")
if !info.Privileged && (info.Snowflake != flake) {
context.JSON(http.StatusForbidden, api.Denied)
@@ -229,6 +250,7 @@ func registerAPI() {
return
}
+ // Only allow set if user is current user or privileged
flake := context.Param("flake")
if !info.Privileged && (info.Snowflake != flake) {
context.JSON(http.StatusForbidden, api.Denied)
@@ -238,10 +260,18 @@ func registerAPI() {
})
router.GET(api.UserImage, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.UserImages(context.Param("flake")))
})
router.GET(api.SearchField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
tagsPayload := context.Param("tags")
tags := strings.Split(tagsPayload, "!")
context.JSON(http.StatusOK, instance.ImageSearch(tags))
@@ -294,10 +324,18 @@ func registerAPI() {
})
router.GET(api.ImagePage, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.String(http.StatusOK, strconv.Itoa(instance.PageTotal(store.ImageRootPageVariant)))
})
router.GET(api.ImagePageField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
param := context.Param("entry")
entry, err := strconv.Atoi(param)
if err != nil {
@@ -308,6 +346,10 @@ func registerAPI() {
})
router.GET(api.ImagePageImage, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
param := context.Param("entry")
entry, err := strconv.Atoi(param)
if err != nil {
@@ -318,6 +360,10 @@ func registerAPI() {
})
router.GET(api.ImageField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.ImageSnowflake(context.Param("flake")))
})
@@ -364,6 +410,10 @@ func registerAPI() {
})
router.GET(api.ImageFile, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
flake := context.Param("flake")
image, data := instance.ImageData(instance.ImageSnowflakeHash(flake), false)
if image.Snowflake != flake {
@@ -374,6 +424,10 @@ func registerAPI() {
})
router.GET(api.ImagePreview, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
flake := context.Param("flake")
image, data := instance.ImageData(instance.ImageSnowflakeHash(flake), true)
if image.Snowflake != flake {
@@ -384,6 +438,10 @@ func registerAPI() {
})
router.GET(api.ImageTag, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.ImageTags(context.Param("flake")))
})
@@ -422,6 +480,10 @@ func registerAPI() {
})
router.GET(api.Tag, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.Tags())
})
@@ -474,6 +536,10 @@ func registerAPI() {
})
router.GET(api.TagPage, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
tag := context.Param("tag")
if !instance.MatchName(tag) {
context.JSON(http.StatusBadRequest, api.Denied)
@@ -483,6 +549,10 @@ func registerAPI() {
})
router.GET(api.TagPageField, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
tag := context.Param("tag")
if !instance.MatchName(tag) {
context.JSON(http.StatusBadRequest, api.Denied)
@@ -499,6 +569,10 @@ func registerAPI() {
})
router.GET(api.TagPageImage, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
tag := context.Param("tag")
if !instance.MatchName(tag) {
context.JSON(http.StatusBadRequest, api.Denied)
@@ -515,6 +589,10 @@ func registerAPI() {
})
router.GET(api.TagInfo, func(context *gin.Context) {
+ if !privateAccessible(context) {
+ return
+ }
+
context.JSON(http.StatusOK, instance.TagInfo(context.Param("tag")))
})
@@ -541,6 +619,19 @@ func registerAPI() {
})
}
+func privateAccessible(context *gin.Context) bool {
+ if !instance.Private {
+ return true
+ }
+
+ if _, ok := getUser(context); !ok {
+ context.JSON(http.StatusForbidden, api.Denied)
+ return false
+ }
+
+ return true
+}
+
func getUser(context *gin.Context) (store.User, bool) {
if instance.SingleUser {
return instance.User(instance.InitialUser), true
diff --git a/api/paths.go b/api/paths.go
index 13fd584..4caec4a 100644
--- a/api/paths.go
+++ b/api/paths.go
@@ -3,6 +3,7 @@ package api
const (
Base = "/api"
SingleUser = Base + "/single_user"
+ Private = Base + "/private"
Image = Base + "/image"
ImagePage = Image + "/page"
ImagePageField = ImagePage + "/:entry"
diff --git a/client/remote.go b/client/remote.go
index 4f14410..4f3248a 100644
--- a/client/remote.go
+++ b/client/remote.go
@@ -9,11 +9,12 @@ import (
// Remote represents a remote image board server.
type Remote struct {
- url string
- single bool
- secret string
- client *http.Client
- user *api.UserPayload
+ url string
+ single bool
+ private bool
+ secret string
+ client *http.Client
+ user *api.UserPayload
store.Info
}
@@ -33,11 +34,19 @@ func (r *Remote) SingleUser() bool {
return r.single
}
+// Private returns whether the Remote is running in private mode.
+func (r *Remote) Private() bool {
+ return r.private
+}
+
// Handshake checks if the server is still online and updates Remote.
func (r *Remote) Handshake() error {
if err := r.fetch(http.MethodGet, api.SingleUser, &r.single, nil); err != nil {
return err
}
+ if err := r.fetch(http.MethodGet, api.Private, &r.private, nil); err != nil {
+ return err
+ }
return r.fetch(http.MethodGet, api.Base, r, nil)
}
diff --git a/config.go b/config.go
index cbe36a1..58867fa 100644
--- a/config.go
+++ b/config.go
@@ -5,6 +5,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"random.chars.jp/git/image-board/store"
+ "strconv"
)
var instance *store.Store
@@ -23,6 +24,7 @@ func configSetup() {
"loglevel": "info",
"store": "./db",
"single-user": true,
+ "private": false,
})
viper.SetDefault("server", map[string]interface{}{
"host": "127.0.0.1",
@@ -58,7 +60,8 @@ func configSetup() {
if viper.GetStringMap("server")["unix"] != serverConfig["unix"] ||
viper.GetStringMap("server")["host"] != serverConfig["host"] ||
viper.GetStringMap("server")["port"] != serverConfig["port"] ||
- viper.GetStringMap("system")["single-user"] != systemConfig["single-user"] {
+ viper.GetStringMap("system")["single-user"] != systemConfig["single-user"] ||
+ viper.GetStringMap("system")["private"] != systemConfig["private"] {
log.Warn("Configuration change requires restart.")
cleanup(true)
return
@@ -76,12 +79,11 @@ func configSetup() {
}
func openStore() {
- path := viper.GetStringMap("system")["store"].(string)
- single, ok := viper.GetStringMap("system")["single-user"].(bool)
- if !ok {
- single = false
- }
- instance = store.New(path, single)
+ path := systemConfig["store"].(string)
+ single := parseBool(systemConfig["single-user"])
+ private := parseBool(systemConfig["private"])
+
+ instance = store.New(path, single, private)
if instance == nil {
log.Fatalf("Error initializing store.")
}
@@ -98,6 +100,26 @@ func openStore() {
}
}
if single {
- log.Infof("Server running in single user mode, all operations are performed as the initial user.")
+ log.Info("Server running in single user mode, all operations are performed as the initial user.")
+ } else if private {
+ log.Info("Server running in private mode, all operations will require authentication.")
+ }
+}
+
+func parseBool(v interface{}) bool {
+ if s, ok := v.(bool); !ok {
+ var sS string
+ if sS, ok = v.(string); !ok {
+ return false
+ } else {
+ if b, err := strconv.ParseBool(sS); err != nil {
+ log.Warnf("Error parsing boolean value, %s", err)
+ return false
+ } else {
+ return b
+ }
+ }
+ } else {
+ return s
}
}
diff --git a/store/store.go b/store/store.go
index 6a5f8e1..b96489b 100644
--- a/store/store.go
+++ b/store/store.go
@@ -33,6 +33,7 @@ type Info struct {
type Store struct {
Path string
SingleUser bool
+ Private bool
Revision int
Compat bool
Register bool
@@ -58,7 +59,7 @@ func init() {
}
// New initialises a new store instance.
-func New(path string, single bool) *Store {
+func New(path string, single, private bool) *Store {
var store *Store
if stat, err := os.Stat(path); err != nil {
log.Infof("Initializing new store at %s.", path)
@@ -66,6 +67,7 @@ func New(path string, single bool) *Store {
store = &Store{
Path: path,
SingleUser: single,
+ Private: private,
Revision: revision,
Compat: runtime.GOOS == "windows",
Register: false,
@@ -101,6 +103,7 @@ func New(path string, single bool) *Store {
store = &Store{
Path: path,
SingleUser: single,
+ Private: private,
Revision: info.Revision,
Compat: info.Compat,
Register: info.Register,