Issue 133 CardDAV Source Could Not Be Saved - edmozley/freeitsm GitHub Wiki
Reported by mbsouth · Fixed in #1722 and #1723
System → Authentication → Add provider, choose CardDAV, fill in the server and sign-in details, and Test - which works:
Connected, and found one address book.
Then Save, and an error. The browser console showed the response the page had tried to read:
<br />
<b>Warning</b>: Undefined array key "carddav_scope" in .../api/system/save_sso_provider.php on line 154
{"success":false,"error":"SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'carddav_scope' cannot be null"}
Nothing was saved. Nobody could add a CardDAV address book, on any installation from 1.8.0 onwards.
A CardDAV source is set up in two steps. The dialog creates it. Then the settings icon on its row opens a page where you choose which address book to read and which contacts to bring in - everyone, or only certain groups. That second part is stored as the scope.
The save code checked the scope like this:
'scope' => in_array(($data['carddav_scope'] ?? 'all'), ['all', 'group', 'category'], true)
? $data['carddav_scope'] : 'all',Read the two halves separately. The test says "if no scope was sent, treat it as all" - and all is valid, so the test passes. The answer then returns $data['carddav_scope'] - the value that was never sent. That is:
- a PHP warning, printed before the JSON, so the page could not read the reply at all, and
- an empty value written to a column that does not allow one, so the database refused the row.
Why it was not caught. For twelve minutes on 12 September the scope picker lived in the dialog itself, and the dialog sent a scope with every save. It then moved to the address book's own page - where it belongs, because you cannot pick groups before you have picked a book - and the dialog stopped sending it. The line above was written for the first arrangement and never revisited for the second. The address book page always sends a scope, so saving from there worked; only a brand-new source, created from the dialog, hit the empty value - and no check covered that screen after the move.
Fixing the crash on its own would have exposed a quieter one. Three screens save a provider through this code, and none of them sends every setting:
| Screen | Did not send |
|---|---|
| the Add / Edit dialog | a CardDAV source's address book, scope and write-back; an LDAP directory's sync settings |
| the address book page | the company it belongs to |
| the LDAP settings page | require verified email and default modules |
The save code treated "not sent" as "empty". So renaming an address book in the dialog would have cleared the chosen address book, gone back to importing everyone, and switched write-back off. Renaming an LDAP directory there switched directory sync off. And saving either settings page quietly moved the source back to Global from whichever company it belonged to. The address book page also always switched a source back on, even one you had turned off.
- The scope is read once, and the default is what gets stored when nothing was sent.
- When a provider is updated, a setting the request does not mention keeps the value it already had. This applies only while the provider stays the same type - changing an LDAP directory into an address book still clears the LDAP settings, so nothing from the old type is left behind and quietly in force.
- The address book page no longer sends enabled, so it cannot turn a disabled source back on.
| File | Change |
|---|---|
api/system/save_sso_provider.php |
the scope fix; stored values kept for anything not sent |
system/sso/carddav.php |
stopped sending enabled
|
Through the real save endpoint, with exactly what each screen sends:
- Before the fix, the dialog's payload reproduced the reported warning and database error word for word.
- After, 13 checks pass: adding from the dialog; choosing a book, groups and write-back on the address book page without losing the company; renaming and disabling in the dialog without losing the book; saving the page again without turning the source back on; the same round trip for an LDAP directory without losing sync, default modules or verified email; switching a provider's type still clearing the old settings; and an update to a provider that does not exist being refused.