diff --git a/OpenApi.yml b/OpenApi.yml index 9fca958..5ce7ac1 100644 --- a/OpenApi.yml +++ b/OpenApi.yml @@ -330,6 +330,8 @@ components: type: array items: $ref: '#/components/schemas/RoomMount' + policies: + $ref: '#/components/schemas/Policies' RoomStats: type: object @@ -360,3 +362,30 @@ components: muted: type: boolean example: false + + Policies: + type: object + properties: + homepage: + type: string + example: https://google.com + extensions: + type: array + items: + $ref: '#/components/schemas/Extension' + developer_tools: + type: boolean + example: true + persistent_data: + type: boolean + example: false + + Extension: + type: object + properties: + id: + type: string + example: uBlock0@raymondhill.net + url: + type: string + example: https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi diff --git a/client/src/api/.openapi-generator/VERSION b/client/src/api/.openapi-generator/VERSION index 6555596..862529f 100644 --- a/client/src/api/.openapi-generator/VERSION +++ b/client/src/api/.openapi-generator/VERSION @@ -1 +1 @@ -5.2.0-SNAPSHOT \ No newline at end of file +5.2.1-SNAPSHOT \ No newline at end of file diff --git a/client/src/api/api.ts b/client/src/api/api.ts index bce8ee1..6d87e92 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -21,6 +21,56 @@ import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObj // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from './base'; +/** + * + * @export + * @interface Extension + */ +export interface Extension { + /** + * + * @type {string} + * @memberof Extension + */ + id?: string; + /** + * + * @type {string} + * @memberof Extension + */ + url?: string; +} +/** + * + * @export + * @interface Policies + */ +export interface Policies { + /** + * + * @type {string} + * @memberof Policies + */ + homepage?: string; + /** + * + * @type {Array} + * @memberof Policies + */ + extensions?: Array; + /** + * + * @type {boolean} + * @memberof Policies + */ + developer_tools?: boolean; + /** + * + * @type {boolean} + * @memberof Policies + */ + persistent_data?: boolean; +} /** * * @export @@ -245,6 +295,12 @@ export interface RoomSettings { * @memberof RoomSettings */ mounts?: Array; + /** + * + * @type {Policies} + * @memberof RoomSettings + */ + policies?: Policies; } /** * diff --git a/internal/room/manager.go b/internal/room/manager.go index e1d8ab7..c2c5d1c 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" "os" "path" "path/filepath" @@ -21,6 +22,7 @@ import ( "github.com/rs/zerolog/log" "m1k1o/neko_rooms/internal/config" + "m1k1o/neko_rooms/internal/policies" "m1k1o/neko_rooms/internal/types" "m1k1o/neko_rooms/internal/utils" ) @@ -194,6 +196,46 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro env = append(env, fmt.Sprintf("NEKO_NAT1TO1=%s", strings.Join(manager.config.NAT1To1IPs, ","))) } + // browser policies + if settings.Policies != nil { + if !manager.config.StorageEnabled { + return "", fmt.Errorf("policies cannot be specified, because storage is disabled or unavailable") + } + + policyConfig := policies.GetConfig(settings.NekoImage) + if policyConfig == nil { + return "", fmt.Errorf("policy config for this image does not exist") + } + + policyJson, err := policies.Generate(*settings.Policies, policyConfig.Type) + if err != nil { + return "", err + } + + // create policy path (+ also get host path) + policyPath := fmt.Sprintf("/%s-%s-policy.json", roomName, policyConfig.Type) + policyHostPath := path.Join(manager.config.StorageExternal, templateStoragePath, policyPath) + + // create dir if does not exist + if _, err := os.Stat(policyHostPath); os.IsNotExist(err) { + if err := os.MkdirAll(policyHostPath, os.ModePerm); err != nil { + return "", err + } + } + + // write policy to file + if err := ioutil.WriteFile(policyHostPath, []byte(policyJson), 0644); err != nil { + return "", err + } + + // mount policy file + settings.Mounts = append(settings.Mounts, types.RoomMount{ + Type: types.MountTemplate, + HostPath: policyPath, + ContainerPath: policyConfig.Path, + }) + } + mounts := []dockerMount.Mount{} for _, mount := range settings.Mounts { readOnly := false @@ -396,11 +438,33 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro }) } + policiesData := &types.Policies{} + policyConfig := policies.GetConfig(nekoImage) + if policyConfig != nil { + var policyMount *types.RoomMount + for _, mount := range mounts { + if mount.ContainerPath == policyConfig.Path { + policyMount = &mount + break + } + } + + // TODO: Refactor. + if policyMount != nil { + if _, err := os.Stat(policyMount.HostPath); !os.IsNotExist(err) { + if data, err := ioutil.ReadFile(policyMount.HostPath); err == nil { + policiesData, _ = policies.Parse(string(data), policyConfig.Type) + } + } + } + } + settings := types.RoomSettings{ Name: roomName, NekoImage: nekoImage, MaxConnections: epr.Max - epr.Min + 1, Mounts: mounts, + Policies: policiesData, } err = settings.FromEnv(container.Config.Env) diff --git a/internal/types/room.go b/internal/types/room.go index f6808af..f06c323 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -75,6 +75,8 @@ type RoomSettings struct { Envs map[string]string `json:"envs"` Mounts []RoomMount `json:"mounts"` + + Policies *Policies `json:"policies,omitempty"` } func (settings *RoomSettings) ToEnv() []string {