Repository: owncloud/ocis
Pull request #6133 (2023-04-25 07:51:15)

enhancement: add mimetype to file extension rego function
<!--
Thanks for submitting a change to ownCloud!

This is the bug tracker for the oCIS component. Find other components at https://github.com/owncloud/core/blob/master/.github/CONTRIBUTING.md#guidelines

For fixing potential security issues please see https://owncloud.org/security/

To make it possible for us to get your change reviewed and merged please carefully fill out the requested information below.

Please note that any kind of change needs first be submitted to the master branch which holds the next version of oCIS.

Please set the following labels:

- Set label "Status:Needs-Review" for review or "Status:In-Progress" if the PR still has open tasks.
- Assignment: assign to self
- Reviewers: pick at least one
-->

## Description
add rego function to detect the resource extension by mimetype, at the same time this pr introduces a custom ocis namespace for the rego functions.

## How Has This Been Tested?
- local installation 
- unit tests

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [ ] Tests only (no source changes)

## Checklist:
<!-- Tick the checkboxes when done. -->
<!-- Raise documentation ticket in owncloud.github.io/ -->
- [X] Code changes
- [X] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 



Patch:
diff --git a/changelog/unreleased/add-request-id-response.md b/changelog/unreleased/add-request-id-response.md
new file mode 100644
index 00000000000..b75f0cd0c4b
--- /dev/null
+++ b/changelog/unreleased/add-request-id-response.md
@@ -0,0 +1,5 @@
+Enhancement: Add X-Request-Id to all responses
+
+We added the X-Request-Id to all responses to increase the debuggability of the platform.
+
+https://github.com/owncloud/ocis/pull/6715
diff --git a/changelog/unreleased/enhancement-add-postprocessing-mimetype-to-extension.md b/changelog/unreleased/enhancement-add-postprocessing-mimetype-to-extension.md
new file mode 100644
index 00000000000..3e99e37afee
--- /dev/null
+++ b/changelog/unreleased/enhancement-add-postprocessing-mimetype-to-extension.md
@@ -0,0 +1,6 @@
+Enhancement: Add postprocessing mimetype to extension helper
+
+Add rego helper to resolve extensions from mimetype `ocis.mimetype.extensions(mimetype)`.
+Besides that, a rego print helper is included also `print("PRINT MESSAGE EXAMPLE")`
+
+https://github.com/owncloud/ocis/pull/6133
diff --git a/deployments/examples/service_policies/policies/postprocessing.rego b/deployments/examples/service_policies/policies/postprocessing.rego
index 4dad89f4142..ea784889a33 100644
--- a/deployments/examples/service_policies/policies/postprocessing.rego
+++ b/deployments/examples/service_policies/policies/postprocessing.rego
@@ -6,5 +6,12 @@ import data.utils
 default granted := true
 
 granted = false if {
-    not utils.collection_contains(utils.ALLOWED_FILE_EXTENSIONS, input.resource.name)
+    not utils.is_extension_allowed(input.resource.name)
+}
+
+granted = false if {
+    bytes := ocis.resource.download(input.resource.url)
+    mimetype := ocis.mimetype.detect(bytes)
+
+    not utils.is_mimetype_allowed(mimetype)
 }
diff --git a/deployments/examples/service_policies/policies/proxy.rego b/deployments/examples/service_policies/policies/proxy.rego
index aae711c04a3..076bb280192 100644
--- a/deployments/examples/service_policies/policies/proxy.rego
+++ b/deployments/examples/service_policies/policies/proxy.rego
@@ -6,13 +6,14 @@ import data.utils
 default granted := true
 
 granted = false if {
-    utils.is_request_type_put
-    not input.request.path == "/data"
-    not utils.collection_contains(utils.ALLOWED_FILE_EXTENSIONS, input.request.path)
+    print("PRINT MESSAGE EXAMPLE")
+    input.request.method == "PUT"
+    not startswith(input.request.path, "/ocs")
+    not utils.is_extension_allowed(input.request.path)
 }
 
 granted = false if {
-    utils.is_request_type_post
+    input.request.method == "POST"
     startswith(input.request.path, "/remote.php")
-    not utils.collection_contains(utils.ALLOWED_FILE_EXTENSIONS, input.resource.name)
+    not utils.is_extension_allowed(input.resource.name)
 }
diff --git a/deployments/examples/service_policies/policies/utils.rego b/deployments/examples/service_policies/policies/utils.rego
index 40aea327b00..65a1fe00e55 100644
--- a/deployments/examples/service_policies/policies/utils.rego
+++ b/deployments/examples/service_policies/policies/utils.rego
@@ -1,8 +1,6 @@
 package utils
 
-import future.keywords.if
-
-ALLOWED_FILE_EXTENSIONS := [
+ALLOWED_RESOURCE_EXTENSIONS := [
     ".apk", ".avi", ".bat", ".bmp", ".css", ".csv", ".doc", ".docm", ".docx",
     ".docxf", ".dotx", ".eml", ".epub", ".htm", ".html", ".ipa", ".jar", ".java",
     ".jpg", ".js", ".json", ".mp3", ".mp4", ".msg", ".odp", ".ods", ".odt", ".oform",
@@ -11,43 +9,14 @@ ALLOWED_FILE_EXTENSIONS := [
     ".txt", ".xls", ".xlsm", ".xlsx", ".xltm", ".xltx", ".xml", ".zip", ".md"
 ]
 
-##
-
-is_stage_http {
-    input.stage == "http"
-}
-
-is_stage_pp {
-    input.stage == "pp"
+is_extension_allowed(identifier) {
+     extension := ALLOWED_RESOURCE_EXTENSIONS[_]
+     endswith(identifier, extension)
 }
 
-##
-
-is_user_admin {
-    input.user.username == "admin"
-}
-
-##
-
-is_request_type_put {
-    is_stage_http
-    input.request.method == "PUT"
-}
-
-is_request_type_post {
-    is_stage_http
-    input.request.method == "POST"
-}
-
-is_request_type_mkcol {
-    is_stage_http
-    input.request.method == "MKCOL"
-}
-
-##
-
-collection_contains(collection, source) {
-     current := collection[_]
-     endswith(source, current)
+is_mimetype_allowed(mimetype) {
+     extensions := ocis.mimetype.extensions(mimetype)
+     extension := extensions[_]
+     is_extension_allowed(extension)
 }
 
diff --git a/services/policies/pkg/command/server.go b/services/policies/pkg/command/server.go
index 9c3cb3eb94e..bf23e97eda5 100644
--- a/services/policies/pkg/command/server.go
+++ b/services/policies/pkg/command/server.go
@@ -21,7 +21,7 @@ import (
 	svcProtogen "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/policies/v0"
 	"github.com/owncloud/ocis/v2/services/policies/pkg/config"
 	"github.com/owncloud/ocis/v2/services/policies/pkg/config/parser"
-	"github.com/owncloud/ocis/v2/services/policies/pkg/engine"
+	"github.com/owncloud/ocis/v2/services/policies/pkg/engine/opa"
 	svcEvent "github.com/owncloud/ocis/v2/services/policies/pkg/service/event"
 	svcGRPC "github.com/owncloud/ocis/v2/services/policies/pkg/service/grpc"
 	"github.com/urfave/cli/v2"
@@ -51,11 +51,11 @@ func Server(cfg *config.Config) *cli.Command {
 					log.Pretty(cfg.Log.Pretty),
 					log.Color(cfg.Log.Color),
 					log.File(cfg.Log.File),
-				)
+				).SubloggerWithRequestID(ctx)
 			)
 			defer cancel()
 
-			e, err := engine.NewOPA(cfg.Engine.Timeout, cfg.Engine)
+			e, err := opa.NewOPA(cfg.Engine.Timeout, logger, cfg.Engine)
 			if err != nil {
 				return err
 			}
diff --git a/services/policies/pkg/engine/opa.go b/services/policies/pkg/engine/opa.go
deleted file mode 100644
index e317fc030f7..00000000000
--- a/services/policies/pkg/engine/opa.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package engine
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"net/http"
-	"strings"
-	"time"
-
-	"github.com/cs3org/reva/v2/pkg/rhttp"
-	"github.com/gabriel-vasile/mimetype"
-	"github.com/open-policy-agent/opa/ast"
-	"github.com/open-policy-agent/opa/rego"
-	"github.com/open-policy-agent/opa/types"
-	"github.com/owncloud/ocis/v2/services/policies/pkg/config"
-)
-
-// OPA wraps open policy agent makes it possible to ask if an action is granted.
-type OPA struct {
-	policies []string
-	timeout  time.Duration
-}
-
-// NewOPA returns a ready to use opa engine.
-func NewOPA(timeout time.Duration, conf config.Engine) (OPA, error) {
-	return OPA{
-			policies: conf.Policies,
-			timeout:  timeout,
-		},
-		nil
-}
-
-// Evaluate evaluates the opa policies and returns the result.
-func (o OPA) Evaluate(ctx context.Context, qs string, env Environment) (bool, error) {
-	ctx, cancel := context.WithTimeout(ctx, o.timeout)
-	defer cancel()
-
-	q, err := rego.New(
-		rego.Query(qs),
-		rego.Load(o.policies, nil),
-		GetMimetype,
-		GetResource,
-	).PrepareForEval(ctx)
-	if err != nil {
-		return false, err
-	}
-
-	result, err := q.Eval(ctx, rego.EvalInput(env))
-	if err != nil {
-		return false, err
-	}
-
-	return result.Allowed(), nil
-}
-
-var GetResource = rego.Function1(
-	&rego.Function{
-		Name:             "ocis_get_resource",
-		Decl:             types.NewFunction(types.Args(types.S), types.A),
-		Memoize:          true,
-		Nondeterministic: true,
-	},
-	func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
-		var url string
-
-		if err := ast.As(a.Value, &url); err != nil {
-			return nil, err
-		}
-
-		req, err := http.NewRequest(http.MethodGet, url, nil)
-		if err != nil {
-			return nil, err
-		}
-
-		client := rhttp.GetHTTPClient(rhttp.Insecure(true))
-		res, err := client.Do(req)
-		if err != nil {
-			return nil, err
-		}
-		defer res.Body.Close()
-
-		if res.StatusCode != http.StatusOK {
-			return nil, fmt.Errorf("unexpected status code from Download %v", res.StatusCode)
-		}
-
-		buf := new(bytes.Buffer)
-		if _, err := buf.ReadFrom(res.Body); err != nil {
-			return nil, err
-		}
-
-		v, err := ast.InterfaceToValue(buf.Bytes())
-		if err != nil {
-			return nil, err
-		}
-
-		return ast.NewTerm(v), nil
-	},
-)
-
-var GetMimetype = rego.Function1(
-	&rego.Function{
-		Name:             "ocis_get_mimetype",
-		Decl:             types.NewFunction(types.Args(types.A), types.S),
-		Memoize:          true,
-		Nondeterministic: true,
-	},
-	func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
-		var body []byte
-
-		if err := ast.As(a.Value, &body); err != nil {
-			return nil, err
-		}
-
-		mimeInfo := mimetype.Detect(body).String()
-		detectedMimetype := strings.Split(mimeInfo, ";")[0]
-		v, err := ast.InterfaceToValue(detectedMimetype)
-		if err != nil {
-			return nil, err
-		}
-
-		return ast.NewTerm(v), nil
-	},
-)
diff --git a/services/policies/pkg/engine/opa/engine.go b/services/policies/pkg/engine/opa/engine.go
new file mode 100644
index 00000000000..45bd211bf79
--- /dev/null
+++ b/services/policies/pkg/engine/opa/engine.go
@@ -0,0 +1,61 @@
+package opa
+
+import (
+	"context"
+	"time"
+
+	"github.com/open-policy-agent/opa/rego"
+	"github.com/open-policy-agent/opa/topdown/print"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/policies/pkg/config"
+	"github.com/owncloud/ocis/v2/services/policies/pkg/engine"
+)
+
+// OPA wraps open policy agent makes it possible to ask if an action is granted.
+type OPA struct {
+	printHook print.Hook
+	policies  []string
+	timeout   time.Duration
+}
+
+// NewOPA returns a ready to use opa engine.
+func NewOPA(timeout time.Duration, logger log.Logger, conf config.Engine) (OPA, error) {
+	return OPA{
+			policies:  conf.Policies,
+			timeout:   timeout,
+			printHook: logPrinter{logger: logger},
+		},
+		nil
+}
+
+// Evaluate evaluates the opa policies and returns the result.
+func (o OPA) Evaluate(ctx context.Context, qs string, env engine.Environment) (bool, error) {
+	ctx, cancel := context.WithTimeout(ctx, o.timeout)
+	defer cancel()
+
+	customFns := []func(r *rego.Rego){
+		RFResourceDownload,
+		RFMimetypeDetect,
+		RFMimetypeExtensions,
+	}
+
+	q, err := rego.New(
+		append([]func(r *rego.Rego){
+			rego.Query(qs),
+			rego.Load(o.policies, nil),
+			rego.EnablePrintStatements(true),
+			rego.PrintHook(o.printHook),
+		}, customFns...)...,
+	).PrepareForEval(ctx)
+	if err != nil {
+		return false, err
+	}
+
+	result, err := q.Eval(ctx, rego.EvalInput(env))
+	if err != nil {
+		return false, err
+	}
+
+	return result.Allowed(), nil
+}
diff --git a/services/policies/pkg/engine/opa/opa.go b/services/policies/pkg/engine/opa/opa.go
new file mode 100644
index 00000000000..b012e38dc78
--- /dev/null
+++ b/services/policies/pkg/engine/opa/opa.go
@@ -0,0 +1,16 @@
+package opa
+
+import (
+	"github.com/open-policy-agent/opa/topdown/print"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+)
+
+type logPrinter struct {
+	logger log.Logger
+}
+
+func (lp logPrinter) Print(_ print.Context, msg string) error {
+	lp.logger.Info().Msg(msg)
+	return nil
+}
diff --git a/services/policies/pkg/engine/opa/rf_mimetype.go b/services/policies/pkg/engine/opa/rf_mimetype.go
new file mode 100644
index 00000000000..033d664b660
--- /dev/null
+++ b/services/policies/pkg/engine/opa/rf_mimetype.go
@@ -0,0 +1,59 @@
+package opa
+
+import (
+	"mime"
+	"strings"
+
+	"github.com/gabriel-vasile/mimetype"
+	"github.com/open-policy-agent/opa/ast"
+	"github.com/open-policy-agent/opa/rego"
+	"github.com/open-policy-agent/opa/types"
+)
+
+var RFMimetypeExtensions = rego.Function1(
+	&rego.Function{
+		Name:             "ocis.mimetype.extensions",
+		Decl:             types.NewFunction(types.Args(types.S), types.A),
+		Memoize:          true,
+		Nondeterministic: true,
+	},
+	func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
+		var mt string
+
+		if err := ast.As(a.Value, &mt); err != nil {
+			return nil, err
+		}
+
+		detectedExtensions, err := mime.ExtensionsByType(mt)
+		if err != nil {
+			return nil, err
+		}
+
+		var mimeTerms []*ast.Term
+		for _, extension := range detectedExtensions {
+			mimeTerms = append(mimeTerms, ast.NewTerm(ast.String(extension)))
+		}
+
+		return ast.ArrayTerm(mimeTerms...), nil
+	},
+)
+
+var RFMimetypeDetect = rego.Function1(
+	&rego.Function{
+		Name:             "ocis.mimetype.detect",
+		Decl:             types.NewFunction(types.Args(types.A), types.S),
+		Memoize:          true,
+		Nondeterministic: true,
+	},
+	func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
+		var body []byte
+
+		if err := ast.As(a.Value, &body); err != nil {
+			return nil, err
+		}
+
+		mimetype := mimetype.Detect(body).String()
+
+		return ast.StringTerm(strings.Split(mimetype, ";")[0]), nil
+	},
+)
diff --git a/services/policies/pkg/engine/opa/rf_resource.go b/services/policies/pkg/engine/opa/rf_resource.go
new file mode 100644
index 00000000000..60543c0b83c
--- /dev/null
+++ b/services/policies/pkg/engine/opa/rf_resource.go
@@ -0,0 +1,56 @@
+package opa
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+
+	"github.com/cs3org/reva/v2/pkg/rhttp"
+	"github.com/open-policy-agent/opa/ast"
+	"github.com/open-policy-agent/opa/rego"
+	"github.com/open-policy-agent/opa/types"
+)
+
+var RFResourceDownload = rego.Function1(
+	&rego.Function{
+		Name:             "ocis.resource.download",
+		Decl:             types.NewFunction(types.Args(types.S), types.A),
+		Memoize:          true,
+		Nondeterministic: true,
+	},
+	func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
+		var url string
+
+		if err := ast.As(a.Value, &url); err != nil {
+			return nil, err
+		}
+
+		req, err := http.NewRequest(http.MethodGet, url, nil)
+		if err != nil {
+			return nil, err
+		}
+
+		client := rhttp.GetHTTPClient(rhttp.Insecure(true))
+		res, err := client.Do(req)
+		if err != nil {
+			return nil, err
+		}
+		defer res.Body.Close()
+
+		if res.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("unexpected status code from Download %v", res.StatusCode)
+		}
+
+		buf := new(bytes.Buffer)
+		if _, err := buf.ReadFrom(res.Body); err != nil {
+			return nil, err
+		}
+
+		v, err := ast.InterfaceToValue(buf.Bytes())
+		if err != nil {
+			return nil, err
+		}
+
+		return ast.NewTerm(v), nil
+	},
+)
diff --git a/services/proxy/pkg/middleware/accesslog.go b/services/proxy/pkg/middleware/accesslog.go
index ff41eaf6b86..00afc71c436 100644
--- a/services/proxy/pkg/middleware/accesslog.go
+++ b/services/proxy/pkg/middleware/accesslog.go
@@ -13,12 +13,15 @@ func AccessLog(logger log.Logger) func(http.Handler) http.Handler {
 	return func(next http.Handler) http.Handler {
 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			start := time.Now()
+			requestID := middleware.GetReqID(r.Context())
+			// add Request Id to all responses
+			w.Header().Set(middleware.RequestIDHeader, requestID)
 			wrap := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
 			next.ServeHTTP(wrap, r)
 
 			logger.Info().
 				Str("proto", r.Proto).
-				Str(log.RequestIDString, middleware.GetReqID(r.Context())).
+				Str(log.RequestIDString, requestID).
 				Str("remote-addr", r.RemoteAddr).
 				Str("method", r.Method).
 				Int("status", wrap.Status()).


Test patch:
diff --git a/services/policies/pkg/engine/opa/opa_suite_test.go b/services/policies/pkg/engine/opa/opa_suite_test.go
new file mode 100644
index 00000000000..4ec6eb1398c
--- /dev/null
+++ b/services/policies/pkg/engine/opa/opa_suite_test.go
@@ -0,0 +1,13 @@
+package opa_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestOpa(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Opa Suite")
+}
diff --git a/services/policies/pkg/engine/opa/rf_mimetype_test.go b/services/policies/pkg/engine/opa/rf_mimetype_test.go
new file mode 100644
index 00000000000..404dc2b456d
--- /dev/null
+++ b/services/policies/pkg/engine/opa/rf_mimetype_test.go
@@ -0,0 +1,31 @@
+package opa_test
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/open-policy-agent/opa/rego"
+
+	"github.com/owncloud/ocis/v2/services/policies/pkg/engine/opa"
+)
+
+var _ = Describe("opa ocis mimetype functions", func() {
+	Describe("ocis.mimetype.detect", func() {
+		It("detects the mimetype", func() {
+			r := rego.New(rego.Query(`ocis.mimetype.detect("")`), opa.RFMimetypeDetect)
+			rs, err := r.Eval(context.Background())
+			Expect(err).ToNot(HaveOccurred())
+			Expect(rs[0].Expressions[0].String()).To(Equal("text/plain"))
+		})
+	})
+
+	Describe("ocis.mimetype.extension_for_mimetype", func() {
+		It("provides matching extensions", func() {
+			r := rego.New(rego.Query(`ocis.mimetype.extensions("application/pdf")`), opa.RFMimetypeExtensions)
+			rs, err := r.Eval(context.Background())
+			Expect(err).ToNot(HaveOccurred())
+			Expect(rs[0].Expressions[0].String()).To(Equal("[.pdf]"))
+		})
+	})
+})
diff --git a/services/policies/pkg/engine/opa/rf_resource_test.go b/services/policies/pkg/engine/opa/rf_resource_test.go
new file mode 100644
index 00000000000..4c5c2fe31ac
--- /dev/null
+++ b/services/policies/pkg/engine/opa/rf_resource_test.go
@@ -0,0 +1,36 @@
+package opa_test
+
+import (
+	"context"
+	"encoding/base64"
+	"net/http"
+	"net/http/httptest"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/open-policy-agent/opa/rego"
+
+	"github.com/owncloud/ocis/v2/services/policies/pkg/engine/opa"
+)
+
+var _ = Describe("opa ocis resource functions", func() {
+	Describe("ocis.resource.download", func() {
+		It("downloads reva resources", func() {
+			ts := []byte("Lorem Ipsum is simply dummy text of the printing and typesetting")
+			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				w.Write(ts)
+			}))
+			defer srv.Close()
+
+			r := rego.New(rego.Query(`ocis.resource.download("`+srv.URL+`")`), opa.RFResourceDownload)
+			rs, err := r.Eval(context.Background())
+			Expect(err).ToNot(HaveOccurred())
+
+			data, err := base64.StdEncoding.DecodeString(rs[0].Expressions[0].String())
+			Expect(err).ToNot(HaveOccurred())
+
+			Expect(data).To(Equal(ts))
+
+		})
+	})
+})
diff --git a/services/policies/pkg/engine/opa_test.go b/services/policies/pkg/engine/opa_test.go
deleted file mode 100644
index 0e0ff29e0ec..00000000000
--- a/services/policies/pkg/engine/opa_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package engine_test
-
-import (
-	"context"
-	"encoding/base64"
-	"net/http"
-	"net/http/httptest"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	"github.com/open-policy-agent/opa/rego"
-	"github.com/owncloud/ocis/v2/services/policies/pkg/engine"
-)
-
-var _ = Describe("Opa", func() {
-	Describe("Custom OPA function", func() {
-		Describe("GetResource", func() {
-			It("loads reva resources", func() {
-				ts := []byte("Lorem Ipsum is simply dummy text of the printing and typesetting")
-				srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-					w.Write(ts)
-				}))
-				defer srv.Close()
-
-				r := rego.New(rego.Query(`ocis_get_resource("`+srv.URL+`")`), engine.GetResource)
-				rs, err := r.Eval(context.Background())
-				Expect(err).ToNot(HaveOccurred())
-
-				data, err := base64.StdEncoding.DecodeString(rs[0].Expressions[0].String())
-				Expect(err).ToNot(HaveOccurred())
-
-				Expect(data).To(Equal(ts))
-
-			})
-		})
-
-		Describe("GetMimetype", func() {
-			It("is defined and returns a mimetype", func() {
-				r := rego.New(rego.Query(`ocis_get_mimetype("")`), engine.GetMimetype)
-				rs, err := r.Eval(context.Background())
-				Expect(err).ToNot(HaveOccurred())
-				Expect(rs[0].Expressions[0].String()).To(Equal("text/plain"))
-			})
-		})
-	})
-})