View | Details | Raw Unified | Return to bug 39224
Collapse All | Expand All

(-)a/t/cypress/integration/Admin/IdentityProviders_spec.ts (-1 / +355 lines)
Line 0 Link Here
0
- 
1
import { mount } from "@cypress/vue";
2
3
function get_provider() {
4
    return {
5
        identity_provider_id: 1,
6
        code: "test_oauth",
7
        description: "Test OAuth Provider",
8
        protocol: "OAuth",
9
        enabled: true,
10
        icon_url: null,
11
        config: {
12
            key: "my_client_id",
13
            secret: "my_client_secret",
14
            authorize_url: "https://idp.example.com/authorize",
15
            token_url: "https://idp.example.com/token",
16
            userinfo_url: "",
17
            scope: "",
18
        },
19
        domains: [],
20
        hostnames: [],
21
        mappings: [],
22
    };
23
}
24
25
describe("Identity providers", () => {
26
    beforeEach(() => {
27
        cy.login();
28
        cy.title().should("eq", "Koha staff interface");
29
        // The provider component fetches all known hostnames on mount
30
        cy.intercept("GET", "/api/v1/auth/hostnames*", []);
31
    });
32
33
    it("List providers", () => {
34
        // GET returns empty list
35
        cy.intercept("GET", "/api/v1/auth/identity_providers*", []);
36
        cy.visit("/cgi-bin/koha/admin/identity_providers");
37
        cy.get("#providers_list").contains(
38
            "There are no identity providers configured"
39
        );
40
41
        // GET returns something
42
        let provider = get_provider();
43
        let providers = [provider];
44
45
        cy.intercept("GET", "/api/v1/auth/identity_providers*", {
46
            statusCode: 200,
47
            body: providers,
48
            headers: {
49
                "X-Base-Total-Count": "1",
50
                "X-Total-Count": "1",
51
            },
52
        });
53
        cy.intercept("GET", "/api/v1/auth/identity_providers/*", provider);
54
        cy.visit("/cgi-bin/koha/admin/identity_providers/");
55
        cy.get("#providers_list").contains("Showing 1 to 1 of 1 entries");
56
    });
57
58
    it("Add provider", () => {
59
        cy.intercept("GET", "/api/v1/auth/identity_providers**", {
60
            statusCode: 200,
61
            body: [],
62
        });
63
64
        cy.visit("/cgi-bin/koha/admin/identity_providers");
65
        cy.contains("New identity provider").click();
66
        cy.get("#providers_add h2").contains("New identity provider");
67
        cy.left_menu_active_item_is("Identity providers");
68
69
        // Required fields are validated
70
        cy.get("#providers_add").contains("Save").click();
71
        cy.get("input:invalid,textarea:invalid,select:invalid").should(
72
            "have.length.gte",
73
            1
74
        );
75
76
        cy.get("#code").type("test_oauth");
77
        cy.get("#description").type("Test OAuth Provider");
78
79
        // Selecting OAuth shows OAuth-specific config fields
80
        cy.get("#protocol .vs__search").type("OAuth{enter}", { force: true });
81
        cy.get("#_config_key").should("be.visible");
82
        cy.get("#_config_secret").should("be.visible");
83
        cy.get("#_config_authorize_url").should("be.visible");
84
        cy.get("#_config_token_url").should("be.visible");
85
        // OIDC-only field should not be visible
86
        cy.get("#_config_well_known_url").should("not.exist");
87
88
        cy.get("#_config_key").type("my_client_id");
89
        cy.get("#_config_secret").type("my_client_secret");
90
        cy.get("#_config_authorize_url").type(
91
            "https://idp.example.com/authorize"
92
        );
93
        cy.get("#_config_token_url").type("https://idp.example.com/token");
94
95
        // Add a mapping relationship
96
        cy.contains("Add mapping").click();
97
        cy.get("#mappings_0 #mappings_provider_field_0").type("email");
98
99
        // Add a domain rule
100
        cy.contains("Add domain rule").click();
101
        cy.get("#domains_0 #domains_domain_0").type("library.org");
102
103
        // Submit the form, get 500
104
        cy.intercept("POST", "/api/v1/auth/identity_providers", {
105
            statusCode: 500,
106
        });
107
        cy.get("#providers_add").contains("Save").click();
108
        cy.get("main div[class='alert alert-warning']").contains(
109
            "Something went wrong: Error: Internal Server Error"
110
        );
111
112
        // Submit the form, success!
113
        let provider = get_provider();
114
        cy.intercept("POST", "/api/v1/auth/identity_providers", {
115
            statusCode: 201,
116
            body: provider,
117
        });
118
        cy.intercept("POST", "/api/v1/auth/identity_providers/*/mappings", {
119
            statusCode: 201,
120
            body: {},
121
        });
122
        cy.intercept("POST", "/api/v1/auth/identity_providers/*/domains", {
123
            statusCode: 201,
124
            body: {},
125
        });
126
        cy.get("#providers_add").contains("Save").click();
127
        cy.get("main div[class='alert alert-info']").contains(
128
            "Identity provider created"
129
        );
130
    });
131
132
    it("Add provider - OIDC protocol shows correct fields", () => {
133
        cy.intercept("GET", "/api/v1/auth/identity_providers**", {
134
            statusCode: 200,
135
            body: [],
136
        });
137
138
        cy.visit("/cgi-bin/koha/admin/identity_providers/add");
139
140
        // Select OIDC - should show well_known_url, hide OAuth-specific fields
141
        cy.get("#protocol .vs__search").type("OIDC{enter}", { force: true });
142
        cy.get("#_config_well_known_url").should("be.visible");
143
        cy.get("#_config_authorize_url").should("not.exist");
144
        cy.get("#_config_token_url").should("not.exist");
145
    });
146
147
    it("Add provider - matchpoint validation", () => {
148
        cy.intercept("GET", "/api/v1/auth/identity_providers**", {
149
            statusCode: 200,
150
            body: [],
151
        });
152
        cy.intercept("GET", "/api/v1/auth/hostnames*", [
153
            { hostname_id: 1, hostname: "library.example.com" },
154
        ]);
155
156
        cy.visit("/cgi-bin/koha/admin/identity_providers/add");
157
        cy.get("#code").type("test_oauth");
158
        cy.get("#description").type("Test OAuth Provider");
159
        cy.get("#protocol .vs__search").type("OAuth{enter}", { force: true });
160
        cy.get("#_config_key").type("my_client_id");
161
        cy.get("#_config_secret").type("my_client_secret");
162
        cy.get("#_config_authorize_url").type(
163
            "https://idp.example.com/authorize"
164
        );
165
        cy.get("#_config_token_url").type("https://idp.example.com/token");
166
167
        // Add a hostname with a matchpoint but no corresponding mapping
168
        cy.contains("Add hostname").click();
169
        cy.get("#hostnames_0 #hostnames_hostname_0 .vs__search").type(
170
            "library.example.com{enter}",
171
            { force: true }
172
        );
173
        cy.get("#hostnames_0 #hostnames_matchpoint_0 .vs__search").type(
174
            "Email{enter}",
175
            { force: true }
176
        );
177
178
        // Submit without a matching mapping → should show validation error
179
        cy.intercept("POST", "/api/v1/auth/identity_providers", {
180
            statusCode: 201,
181
            body: { identity_provider_id: 1 },
182
        });
183
        cy.get("#providers_add").contains("Save").click();
184
        cy.get("main div[class='alert alert-warning']").contains(
185
            "The selected matchpoint must have a corresponding attribute mapping"
186
        );
187
    });
188
189
    it("Edit provider", () => {
190
        let provider = get_provider();
191
        let providers = [provider];
192
193
        // Intercept the list load
194
        cy.intercept("GET", "/api/v1/auth/identity_providers?_page*", {
195
            statusCode: 200,
196
            body: providers,
197
            headers: {
198
                "X-Base-Total-Count": "1",
199
                "X-Total-Count": "1",
200
            },
201
        }).as("get-providers-list");
202
203
        cy.visit("/cgi-bin/koha/admin/identity_providers");
204
        cy.wait("@get-providers-list");
205
206
        // Intercept the single-provider fetch after clicking Edit
207
        cy.intercept("GET", "/api/v1/auth/identity_providers/*", provider).as(
208
            "get-provider"
209
        );
210
211
        cy.get("#providers_list table tbody tr:first").contains("Edit").click();
212
        cy.wait("@get-provider");
213
        cy.wait(500); // Allow Vue to populate the form
214
        cy.get("#providers_add h2").contains("Edit identity provider");
215
        cy.left_menu_active_item_is("Identity providers");
216
217
        // Form is pre-filled correctly
218
        cy.get("#code").should("have.value", provider.code);
219
        cy.get("#description").should("have.value", provider.description);
220
221
        // Submit the form, get 500
222
        cy.intercept("PUT", "/api/v1/auth/identity_providers/*", req => {
223
            req.reply({
224
                statusCode: 500,
225
                delay: 1000,
226
            });
227
        }).as("edit-provider");
228
        cy.get("#providers_add").contains("Save").click();
229
        cy.get("main div[class='modal_centered']").contains("Submitting...");
230
        cy.wait("@edit-provider");
231
        cy.get("main div[class='alert alert-warning']").contains(
232
            "Something went wrong: Error: Internal Server Error"
233
        );
234
235
        // Submit the form, success!
236
        cy.intercept("PUT", "/api/v1/auth/identity_providers/*", {
237
            statusCode: 200,
238
            body: provider,
239
        });
240
        cy.get("#providers_add").contains("Save").click();
241
        cy.get("main div[class='alert alert-info']").contains(
242
            "Identity provider updated"
243
        );
244
    });
245
246
    it("Show provider", () => {
247
        let provider = get_provider();
248
        let providers = [provider];
249
250
        cy.intercept("GET", "/api/v1/auth/identity_providers*", {
251
            statusCode: 200,
252
            body: providers,
253
            headers: {
254
                "X-Base-Total-Count": "1",
255
                "X-Total-Count": "1",
256
            },
257
        });
258
        cy.intercept("GET", "/api/v1/auth/identity_providers/*", provider).as(
259
            "get-provider"
260
        );
261
262
        cy.visit("/cgi-bin/koha/admin/identity_providers");
263
        let code_link = cy.get(
264
            "#providers_list table tbody tr:first td:first a"
265
        );
266
        code_link.should("have.text", provider.code);
267
        code_link.click();
268
        cy.wait("@get-provider");
269
        cy.get("#providers_show h2").contains(
270
            "Identity provider #" + provider.identity_provider_id
271
        );
272
        cy.left_menu_active_item_is("Identity providers");
273
    });
274
275
    it("Delete provider", () => {
276
        let provider = get_provider();
277
        let providers = [provider];
278
279
        // Delete from list
280
        cy.intercept("GET", "/api/v1/auth/identity_providers*", {
281
            statusCode: 200,
282
            body: providers,
283
            headers: {
284
                "X-Base-Total-Count": "1",
285
                "X-Total-Count": "1",
286
            },
287
        });
288
        cy.intercept("GET", "/api/v1/auth/identity_providers/*", provider);
289
        cy.visit("/cgi-bin/koha/admin/identity_providers");
290
291
        cy.get("#providers_list table tbody tr:first")
292
            .contains("Delete")
293
            .click();
294
        cy.get(".alert-warning.confirmation h1").contains(
295
            "delete this identity provider"
296
        );
297
        cy.contains(provider.code);
298
299
        // Accept the confirmation dialog, get 500
300
        cy.intercept("DELETE", "/api/v1/auth/identity_providers/*", {
301
            statusCode: 500,
302
        });
303
        cy.contains("Yes, delete").click();
304
        cy.get("main div[class='alert alert-warning']").contains(
305
            "Something went wrong: Error: Internal Server Error"
306
        );
307
308
        // Accept the confirmation dialog, success!
309
        cy.intercept("DELETE", "/api/v1/auth/identity_providers/*", {
310
            statusCode: 204,
311
            body: null,
312
        });
313
        cy.get("#providers_list table tbody tr:first")
314
            .contains("Delete")
315
            .click();
316
        cy.get(".alert-warning.confirmation h1").contains(
317
            "delete this identity provider"
318
        );
319
        cy.contains("Yes, delete").click();
320
        cy.get("main div[class='alert alert-info']").contains(
321
            "Identity provider deleted"
322
        );
323
324
        // Delete from show
325
        cy.intercept("GET", "/api/v1/auth/identity_providers*", {
326
            statusCode: 200,
327
            body: providers,
328
            headers: {
329
                "X-Base-Total-Count": "1",
330
                "X-Total-Count": "1",
331
            },
332
        });
333
        cy.intercept("GET", "/api/v1/auth/identity_providers/*", provider).as(
334
            "get-provider"
335
        );
336
        cy.visit("/cgi-bin/koha/admin/identity_providers");
337
        let code_link = cy.get(
338
            "#providers_list table tbody tr:first td:first a"
339
        );
340
        code_link.click();
341
        cy.wait("@get-provider");
342
        cy.get("#providers_show h2").contains(
343
            "Identity provider #" + provider.identity_provider_id
344
        );
345
346
        cy.get("#providers_show #toolbar").contains("Delete").click();
347
        cy.get(".alert-warning.confirmation h1").contains(
348
            "delete this identity provider"
349
        );
350
        cy.contains("Yes, delete").click();
351
352
        // Make sure we return to the list after deleting from show
353
        cy.get("#providers_list table tbody tr:first");
354
    });
355
});

Return to bug 39224