auth-proxy: verify group membership (#105)

* auth-proxy: verify group membership

* memberships: install memberships app and use it in few apps

* app-repo: render auth

* installer: always use external dependencies option in app configs

* installer: fix auth handling

* auth-proxy: configure membership-addr and groups flags in helm chart

* installer: fix indentation

* app-manager: fix how auth block is rendered

---------

Co-authored-by: Giorgi Lekveishvili <lekva@gl-mbp-m1-max.local>
diff --git a/core/installer/schema.go b/core/installer/schema.go
index 249190b..a692ecf 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -16,6 +16,7 @@
 	KindString       = 1
 	KindStruct       = 2
 	KindNetwork      = 3
+	KindAuth         = 5
 	KindNumber       = 4
 )
 
@@ -24,6 +25,13 @@
 	Fields() map[string]Schema
 }
 
+var AuthSchema Schema = structSchema{
+	fields: map[string]Schema{
+		"enabled": basicSchema{KindBoolean},
+		"groups":  basicSchema{KindString},
+	},
+}
+
 const networkSchema = `
 #Network: {
     name: string
@@ -50,6 +58,30 @@
 	return false
 }
 
+const authSchema = `
+#Auth: {
+    enabled: bool | false
+    groups: string | *""
+}
+
+value: { %s }
+`
+
+func isAuth(v cue.Value) bool {
+	if v.Value().Kind() != cue.StructKind {
+		return false
+	}
+	s := fmt.Sprintf(authSchema, fmt.Sprintf("%#v", v))
+	c := cuecontext.New()
+	u := c.CompileString(s)
+	auth := u.LookupPath(cue.ParsePath("#Auth"))
+	vv := u.LookupPath(cue.ParsePath("value"))
+	if err := auth.Subsume(vv); err == nil {
+		return true
+	}
+	return false
+}
+
 type basicSchema struct {
 	kind Kind
 }
@@ -85,6 +117,8 @@
 	case cue.StructKind:
 		if isNetwork(v) {
 			return basicSchema{KindNetwork}, nil
+		} else if isAuth(v) {
+			return basicSchema{KindAuth}, nil
 		}
 		s := structSchema{make(map[string]Schema)}
 		f, err := v.Fields(cue.Schema())