Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.theveloper.pixelplay.data.navidrome.model

import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import com.theveloper.pixelplay.data.stream.CloudStreamSecurity

/**
* Represents authentication credentials for a Navidrome/Subsonic server.
Expand Down Expand Up @@ -48,7 +49,18 @@ data class NavidromeCredentials(
* Returns the parsed and normalized server URL, or null if it is invalid.
*/
val normalizedHttpUrlOrNull: HttpUrl?
get() = serverUrl.trim().trimEnd('/').toHttpUrlOrNull()
get() {
val trimmed = serverUrl.trim().trimEnd('/')
// Auto-prepend https:// if no scheme is provided
val withScheme = if (!trimmed.startsWith("http://", ignoreCase = true) &&
!trimmed.startsWith("https://", ignoreCase = true)
) {
"https://$trimmed"
} else {
trimmed
}
return withScheme.toHttpUrlOrNull()
}

/**
* Returns the normalized server URL (without trailing slash).
Expand All @@ -65,7 +77,14 @@ data class NavidromeCredentials(
return "Server URL must not include embedded credentials."
}
if (requireHttps && !httpUrl.isHttps) {
return "Use an https:// server URL for Navidrome/Subsonic."
val host = httpUrl.host
val isPrivate = host == "localhost" ||
host == "127.0.0.1" ||
host.endsWith(".local") ||
CloudStreamSecurity.isPrivateIpv4Literal(host)
if (!isPrivate) {
return "Use an https:// server URL for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses."
}
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,26 @@ class NavidromeCredentialsTest {
}

@Test
fun `connectionValidationError rejects insecure http urls`() {
fun `connectionValidationError accepts local http urls`() {
val credentials = NavidromeCredentials(
serverUrl = "http://192.168.1.20:4533",
username = "user",
password = "pass"
)

assertNull(credentials.connectionValidationError())
}

@Test
fun `connectionValidationError rejects remote http urls`() {
val credentials = NavidromeCredentials(
serverUrl = "http://music.example.com",
username = "user",
password = "pass"
)

assertEquals(
"Use an https:// server URL for Navidrome/Subsonic.",
"Use an https:// server URL for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses.",
credentials.connectionValidationError()
)
}
Expand Down
Loading