ExApps can Send/SendTo a bell for one Recipient and read group membership and directory OCS as the Requesting user, with Access Gate using UserGroups. Co-authored-by: Cursor <cursoragent@cursor.com>
269 lines
8.0 KiB
Go
269 lines
8.0 KiB
Go
package gonexapp_test
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.neitzel.de/konrad/go-nc-exapp"
|
|
)
|
|
|
|
func authUserFromRequest(r *http.Request) string {
|
|
raw := r.Header.Get("AUTHORIZATION-APP-API")
|
|
decoded, err := base64.StdEncoding.DecodeString(raw)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
parts := strings.SplitN(string(decoded), ":", 2)
|
|
if len(parts) < 1 {
|
|
return ""
|
|
}
|
|
return parts[0]
|
|
}
|
|
|
|
func TestAppAPINotificationsSendPostsForCredentialsUser(t *testing.T) {
|
|
var (
|
|
gotMethod string
|
|
gotPath string
|
|
gotUser string
|
|
gotBody []byte
|
|
)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotMethod = r.Method
|
|
gotPath = r.URL.Path
|
|
gotUser = authUserFromRequest(r)
|
|
gotBody, _ = io.ReadAll(r.Body)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
err := api.Send(gonexapp.Notification{Subject: "Hello"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if gotMethod != http.MethodPost {
|
|
t.Fatalf("method=%q", gotMethod)
|
|
}
|
|
if !strings.Contains(gotPath, "apps/app_api/api/v1/notification") {
|
|
t.Fatalf("path=%q", gotPath)
|
|
}
|
|
if gotUser != "alice" {
|
|
t.Fatalf("auth user=%q", gotUser)
|
|
}
|
|
|
|
var payload struct {
|
|
Params struct {
|
|
Object string `json:"object"`
|
|
ObjectID string `json:"object_id"`
|
|
SubjectType string `json:"subject_type"`
|
|
SubjectParams struct {
|
|
RichSubject string `json:"rich_subject"`
|
|
RichSubjectParams map[string]any `json:"rich_subject_params"`
|
|
} `json:"subject_params"`
|
|
} `json:"params"`
|
|
}
|
|
if err := json.Unmarshal(gotBody, &payload); err != nil {
|
|
t.Fatalf("body: %v\n%s", err, gotBody)
|
|
}
|
|
if payload.Params.Object != "app_api" {
|
|
t.Fatalf("object=%q", payload.Params.Object)
|
|
}
|
|
if payload.Params.ObjectID != "app_api_id" {
|
|
t.Fatalf("object_id=%q", payload.Params.ObjectID)
|
|
}
|
|
if payload.Params.SubjectType != "app_api_ex_app" {
|
|
t.Fatalf("subject_type=%q", payload.Params.SubjectType)
|
|
}
|
|
if payload.Params.SubjectParams.RichSubject != "Hello" {
|
|
t.Fatalf("rich_subject=%q", payload.Params.SubjectParams.RichSubject)
|
|
}
|
|
if payload.Params.SubjectParams.RichSubjectParams == nil {
|
|
t.Fatal("rich_subject_params is null")
|
|
}
|
|
if len(payload.Params.SubjectParams.RichSubjectParams) != 0 {
|
|
t.Fatalf("rich_subject_params=%#v", payload.Params.SubjectParams.RichSubjectParams)
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsSendToImpersonatesGivenUser(t *testing.T) {
|
|
var gotUser string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotUser = authUserFromRequest(r)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
err := api.SendTo("bob", gonexapp.Notification{Subject: "Hello"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if gotUser != "bob" {
|
|
t.Fatalf("auth user=%q, want bob", gotUser)
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsRejectsEmptySubject(t *testing.T) {
|
|
called := false
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
called = true
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
if err := api.Send(gonexapp.Notification{}); err == nil {
|
|
t.Fatal("Send: expected error for empty Subject")
|
|
}
|
|
if err := api.SendTo("bob", gonexapp.Notification{Message: "no subject"}); err == nil {
|
|
t.Fatal("SendTo: expected error for empty Subject")
|
|
}
|
|
if called {
|
|
t.Fatal("OCS was called")
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsSendRejectsEmptyUserID(t *testing.T) {
|
|
called := false
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
called = true
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
if err := api.Send(gonexapp.Notification{Subject: "Hello"}); err == nil {
|
|
t.Fatal("expected error for empty UserID")
|
|
}
|
|
if called {
|
|
t.Fatal("OCS was called")
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsSendToRejectsEmptyUserID(t *testing.T) {
|
|
called := false
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
called = true
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
if err := api.SendTo("", gonexapp.Notification{Subject: "Hello"}); err == nil {
|
|
t.Fatal("expected error for empty userID")
|
|
}
|
|
if called {
|
|
t.Fatal("OCS was called")
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsSendIncludesOptionalMessageAndLink(t *testing.T) {
|
|
var gotBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotBody, _ = io.ReadAll(r.Body)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
err := api.Send(gonexapp.Notification{
|
|
Subject: "Hello",
|
|
Message: "Details here",
|
|
Link: "https://cloud.example/apps/checkdns",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var payload struct {
|
|
Params struct {
|
|
SubjectParams struct {
|
|
RichMessage string `json:"rich_message"`
|
|
RichMessageParams map[string]any `json:"rich_message_params"`
|
|
Link string `json:"link"`
|
|
} `json:"subject_params"`
|
|
} `json:"params"`
|
|
}
|
|
if err := json.Unmarshal(gotBody, &payload); err != nil {
|
|
t.Fatalf("body: %v\n%s", err, gotBody)
|
|
}
|
|
if payload.Params.SubjectParams.RichMessage != "Details here" {
|
|
t.Fatalf("rich_message=%q", payload.Params.SubjectParams.RichMessage)
|
|
}
|
|
if payload.Params.SubjectParams.RichMessageParams == nil {
|
|
t.Fatal("rich_message_params is null")
|
|
}
|
|
if payload.Params.SubjectParams.Link != "https://cloud.example/apps/checkdns" {
|
|
t.Fatalf("link=%q", payload.Params.SubjectParams.Link)
|
|
}
|
|
}
|
|
|
|
func TestAppAPINotificationsSendSurfacesOCSStatus(t *testing.T) {
|
|
for _, code := range []int{http.StatusBadRequest, http.StatusForbidden} {
|
|
t.Run(http.StatusText(code), func(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "nope", code)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cred := gonexapp.Credentials{
|
|
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
|
}
|
|
api := gonexapp.NewAppAPINotifications(cred)
|
|
api.Client = srv.Client()
|
|
api.OCS.Client = srv.Client()
|
|
|
|
err := api.Send(gonexapp.Notification{Subject: "Hello"})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
want := fmt.Sprintf("%d", code)
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Fatalf("error %q missing status %s", err, want)
|
|
}
|
|
})
|
|
}
|
|
}
|