Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d09c70f5 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

More fine-grained WebDAV permissions

* service DB: split readOnly into privWriteContent and privUnbind
* collections: use privWriteContent (DAV:write-content privilege) for read-only detection
* AccountActivity: allow collection deletion only when privUnbind (DAV:unbind privilege) is set
parent 2f0ee8e2
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -61,7 +61,8 @@ class CollectionInfoTest {
            info = CollectionInfo(response)
        }
        assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info?.type)
        assertFalse(info!!.readOnly)
        assertTrue(info!!.privWriteContent)
        assertTrue(info!!.privUnbind)
        assertEquals("My Contacts", info?.displayName)
        assertEquals("My Contacts Description", info?.description)

@@ -87,7 +88,8 @@ class CollectionInfoTest {
            info = CollectionInfo(response)
        }
        assertEquals(CollectionInfo.Type.CALENDAR, info?.type)
        assertTrue(info!!.readOnly)
        assertFalse(info!!.privWriteContent)
        assertFalse(info!!.privUnbind)
        assertNull(info?.displayName)
        assertEquals("My Calendar", info?.description)
        assertEquals(0xFFFF0000.toInt(), info?.color)
@@ -103,7 +105,8 @@ class CollectionInfoTest {
        values.put(Collections.SERVICE_ID, 1)
        values.put(Collections.TYPE, CollectionInfo.Type.CALENDAR.name)
        values.put(Collections.URL, "http://example.com")
        values.put(Collections.READ_ONLY, 1)
        values.put(Collections.PRIV_WRITE_CONTENT, 0)
        values.put(Collections.PRIV_UNBIND, 0)
        values.put(Collections.DISPLAY_NAME, "display name")
        values.put(Collections.DESCRIPTION, "description")
        values.put(Collections.COLOR, 0xFFFF0000)
@@ -117,7 +120,8 @@ class CollectionInfoTest {
        assertEquals(1.toLong(), info.id)
        assertEquals(1.toLong(), info.serviceID)
        assertEquals(HttpUrl.parse("http://example.com/"), info.url)
        assertTrue(info.readOnly)
        assertFalse(info.privWriteContent)
        assertFalse(info.privUnbind)
        assertEquals("display name", info.displayName)
        assertEquals("description", info.description)
        assertEquals(0xFFFF0000.toInt(), info.color)
+2 −2
Original line number Diff line number Diff line
@@ -161,7 +161,7 @@ class DavService: Service() {
                val collections = mutableMapOf<HttpUrl, CollectionInfo>()
                db.query(Collections._TABLE, null, "${Collections.SERVICE_ID}=?", arrayOf(service.toString()), null, null, null)?.use { cursor ->
                    while (cursor.moveToNext()) {
                        val values = ContentValues()
                        val values = ContentValues(cursor.columnCount)
                        DatabaseUtils.cursorRowToContentValues(cursor, values)
                        values.getAsString(Collections.URL)?.let { url ->
                            HttpUrl.parse(url)?.let { collections.put(it, CollectionInfo(values)) }
@@ -303,7 +303,7 @@ class DavService: Service() {
                        val selectedCollections = HashSet<HttpUrl>()
                        collections.values
                                .filter { it.selected }
                                .forEach { (url, _) -> selectedCollections.add(url) }
                                .forEach { (url, _) -> selectedCollections += url }

                        // now refresh collections (taken from home sets)
                        val itHomeSets = homeSets.iterator()
+13 −5
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ data class CollectionInfo(

        var type: Type? = null,

        var readOnly: Boolean = false,
        var privWriteContent: Boolean = true,
        var privUnbind: Boolean = true,
        var forceReadOnly: Boolean = false,
        var displayName: String? = null,
        var description: String? = null,
@@ -68,7 +69,8 @@ data class CollectionInfo(
        }

        dav[CurrentUserPrivilegeSet::class.java]?.let { privilegeSet ->
            readOnly = !privilegeSet.mayWriteContent
            privWriteContent = privilegeSet.mayWriteContent
            privUnbind = privilegeSet.mayUnbind
        }

        dav[DisplayName::class.java]?.let {
@@ -110,7 +112,8 @@ data class CollectionInfo(
            null
        }

        readOnly = values.getAsInteger(Collections.READ_ONLY) != 0
        privWriteContent = values.getAsInteger(Collections.PRIV_WRITE_CONTENT) != 0
        privUnbind = values.getAsInteger(Collections.PRIV_UNBIND) != 0
        forceReadOnly = values.getAsInteger(Collections.FORCE_READ_ONLY) != 0
        displayName = values.getAsString(Collections.DISPLAY_NAME)
        description = values.getAsString(Collections.DESCRIPTION)
@@ -132,7 +135,8 @@ data class CollectionInfo(
        type?.let { values.put(Collections.TYPE, it.name) }

        values.put(Collections.URL, url.toString())
        values.put(Collections.READ_ONLY, if (readOnly) 1 else 0)
        values.put(Collections.PRIV_WRITE_CONTENT, if (privWriteContent) 1 else 0)
        values.put(Collections.PRIV_UNBIND, if (privUnbind) 1 else 0)
        values.put(Collections.FORCE_READ_ONLY, if (forceReadOnly) 1 else 0)
        values.put(Collections.DISPLAY_NAME, displayName)
        values.put(Collections.DESCRIPTION, description)
@@ -176,7 +180,9 @@ data class CollectionInfo(

        dest.writeString(type?.name)

        dest.writeByte(if (readOnly) 1 else 0)
        dest.writeByte(if (privWriteContent) 1 else 0)
        dest.writeByte(if (privUnbind) 1 else 0)

        dest.writeByte(if (forceReadOnly) 1 else 0)
        dest.writeString(displayName)
        dest.writeString(description)
@@ -220,6 +226,8 @@ data class CollectionInfo(
                    parcel.readString()?.let { Type.valueOf(it) },

                    parcel.readByte() != 0.toByte(),
                    parcel.readByte() != 0.toByte(),

                    parcel.readByte() != 0.toByte(),
                    parcel.readString(),
                    parcel.readString(),
+16 −3
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ class ServiceDB {
        const val TYPE = "type"
        const val SERVICE_ID = "serviceID"
        const val URL = "url"
        const val READ_ONLY = "readOnly"
        const val PRIV_WRITE_CONTENT = "privWriteContent"
        const val PRIV_UNBIND = "privUnbind"
        const val FORCE_READ_ONLY = "forceReadOnly"
        const val DISPLAY_NAME = "displayName"
        const val DESCRIPTION = "description"
@@ -75,7 +76,7 @@ class ServiceDB {

        companion object {
            const val DATABASE_NAME = "services.db"
            const val DATABASE_VERSION = 4
            const val DATABASE_VERSION = 5
        }

        override fun onConfigure(db: SQLiteDatabase) {
@@ -104,7 +105,8 @@ class ServiceDB {
                    "${Collections.SERVICE_ID} INTEGER NOT NULL REFERENCES ${Services._TABLE} ON DELETE CASCADE," +
                    "${Collections.TYPE} TEXT NOT NULL," +
                    "${Collections.URL} TEXT NOT NULL," +
                    "${Collections.READ_ONLY} INTEGER DEFAULT 0 NOT NULL," +
                    "${Collections.PRIV_WRITE_CONTENT} INTEGER DEFAULT 0 NOT NULL," +
                    "${Collections.PRIV_UNBIND} INTEGER DEFAULT 0 NOT NULL," +
                    "${Collections.FORCE_READ_ONLY} INTEGER DEFAULT 0 NOT NULL," +
                    "${Collections.DISPLAY_NAME} TEXT NULL," +
                    "${Collections.DESCRIPTION} TEXT NULL," +
@@ -130,6 +132,17 @@ class ServiceDB {
            }
        }

        @Suppress("unused")
        private fun upgrade_4_5(db: SQLiteDatabase) {
            db.execSQL("ALTER TABLE ${Collections._TABLE} ADD COLUMN ${Collections.PRIV_WRITE_CONTENT} INTEGER DEFAULT 0 NOT NULL")
            db.execSQL("UPDATE ${Collections._TABLE} SET ${Collections.PRIV_WRITE_CONTENT}=NOT readOnly")

            db.execSQL("ALTER TABLE ${Collections._TABLE} ADD COLUMN ${Collections.PRIV_UNBIND} INTEGER DEFAULT 0 NOT NULL")
            db.execSQL("UPDATE ${Collections._TABLE} SET ${Collections.PRIV_UNBIND}=NOT readOnly")

            // there's no DROP COLUMN in SQLite, so just keep the "readOnly" column
        }

        @Suppress("unused")
        private fun upgrade_3_4(db: SQLiteDatabase) {
            db.execSQL("ALTER TABLE ${Collections._TABLE} ADD COLUMN ${Collections.FORCE_READ_ONLY} INTEGER DEFAULT 0 NOT NULL")
+2 −2
Original line number Diff line number Diff line
@@ -191,8 +191,8 @@ class LocalAddressBook(
            account = future.result
        }

        Constants.log.info("Address book read-only? = ${info.readOnly}")
        readOnly = info.readOnly || info.forceReadOnly
        Constants.log.info("Address book write permission? = ${info.privWriteContent}")
        readOnly = !info.privWriteContent || info.forceReadOnly

        // make sure it will still be synchronized when contacts are updated
        ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
Loading