mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Use API token authentiation in traccar_server (#155297)
This commit is contained in:
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/traccar",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pytraccar"],
|
||||
"requirements": ["pytraccar==2.1.1", "stringcase==1.2.0"]
|
||||
"requirements": ["pytraccar==3.0.0", "stringcase==1.2.0"]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ from pytraccar import ApiClient
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_TOKEN,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
@@ -18,6 +19,7 @@ from homeassistant.const import (
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
|
||||
@@ -33,6 +35,11 @@ PLATFORMS: list[Platform] = [
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Traccar Server from a config entry."""
|
||||
if CONF_API_TOKEN not in entry.data:
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="migrate_to_api_token",
|
||||
)
|
||||
client_session = async_create_clientsession(
|
||||
hass,
|
||||
cookie_jar=CookieJar(
|
||||
@@ -46,8 +53,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
client_session=client_session,
|
||||
host=entry.data[CONF_HOST],
|
||||
port=entry.data[CONF_PORT],
|
||||
username=entry.data[CONF_USERNAME],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
token=entry.data[CONF_API_TOKEN],
|
||||
ssl=entry.data[CONF_SSL],
|
||||
verify_ssl=entry.data[CONF_VERIFY_SSL],
|
||||
),
|
||||
@@ -90,3 +96,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle an options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
|
||||
if entry.version < 2:
|
||||
# Version 2: Remove username and password, only keep API token
|
||||
data = dict(entry.data)
|
||||
data.pop(CONF_USERNAME, None)
|
||||
data.pop(CONF_PASSWORD, None)
|
||||
hass.config_entries.async_update_entry(entry, data=data, version=2)
|
||||
return True
|
||||
|
||||
@@ -16,11 +16,10 @@ import voluptuous as vol
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import (
|
||||
CONF_API_TOKEN,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_USERNAME,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
@@ -61,10 +60,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
vol.Optional(CONF_PORT, default="8082"): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.TEXT)
|
||||
),
|
||||
vol.Required(CONF_USERNAME): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.EMAIL)
|
||||
),
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
vol.Required(CONF_API_TOKEN): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.PASSWORD)
|
||||
),
|
||||
vol.Optional(CONF_SSL, default=False): BooleanSelector(BooleanSelectorConfig()),
|
||||
@@ -120,16 +116,17 @@ OPTIONS_FLOW = {
|
||||
class TraccarServerConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Traccar Server."""
|
||||
|
||||
VERSION = 2
|
||||
|
||||
async def _get_server_info(self, user_input: dict[str, Any]) -> ServerModel:
|
||||
"""Get server info."""
|
||||
client = ApiClient(
|
||||
client_session=async_get_clientsession(self.hass),
|
||||
host=user_input[CONF_HOST],
|
||||
port=user_input[CONF_PORT],
|
||||
username=user_input[CONF_USERNAME],
|
||||
password=user_input[CONF_PASSWORD],
|
||||
ssl=user_input[CONF_SSL],
|
||||
verify_ssl=user_input[CONF_VERIFY_SSL],
|
||||
token=user_input[CONF_API_TOKEN],
|
||||
)
|
||||
return await client.get_server()
|
||||
|
||||
@@ -201,19 +198,11 @@ class TraccarServerConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
reauth_entry,
|
||||
data_updates=user_input,
|
||||
)
|
||||
username = (
|
||||
user_input[CONF_USERNAME]
|
||||
if user_input
|
||||
else reauth_entry.data[CONF_USERNAME]
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME, default=username): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.EMAIL)
|
||||
),
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
vol.Required(CONF_API_TOKEN): TextSelector(
|
||||
TextSelectorConfig(type=TextSelectorType.PASSWORD)
|
||||
),
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/traccar_server",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["pytraccar==2.1.1"]
|
||||
"requirements": ["pytraccar==3.0.0"]
|
||||
}
|
||||
|
||||
@@ -12,23 +12,21 @@
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
"api_token": "[%key:common::config_flow::data::api_token%]"
|
||||
},
|
||||
"description": "The authentication credentials for {host}:{port} need to be updated."
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_token": "[%key:common::config_flow::data::api_token%]",
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"port": "[%key:common::config_flow::data::port%]",
|
||||
"ssl": "[%key:common::config_flow::data::ssl%]",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of your Traccar Server",
|
||||
"username": "The username (email) you use to log in to your Traccar Server"
|
||||
"api_token": "The API token generated from your account on your Traccar Server",
|
||||
"host": "The hostname or IP address of your Traccar Server"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +60,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"migrate_to_api_token": {
|
||||
"message": "To continue using Traccar Server, you need to migrate to API token based authentication."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2605,7 +2605,7 @@ pytouchlinesl==0.5.0
|
||||
|
||||
# homeassistant.components.traccar
|
||||
# homeassistant.components.traccar_server
|
||||
pytraccar==2.1.1
|
||||
pytraccar==3.0.0
|
||||
|
||||
# homeassistant.components.tradfri
|
||||
pytradfri[async]==9.0.1
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -2166,7 +2166,7 @@ pytouchlinesl==0.5.0
|
||||
|
||||
# homeassistant.components.traccar
|
||||
# homeassistant.components.traccar_server
|
||||
pytraccar==2.1.1
|
||||
pytraccar==3.0.0
|
||||
|
||||
# homeassistant.components.tradfri
|
||||
pytradfri[async]==9.0.1
|
||||
|
||||
@@ -14,11 +14,10 @@ from homeassistant.components.traccar_server.const import (
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_TOKEN,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_USERNAME,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
|
||||
@@ -74,8 +73,7 @@ def mock_config_entry() -> MockConfigEntry:
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: "8082",
|
||||
CONF_USERNAME: "test@example.org",
|
||||
CONF_PASSWORD: "ThisIsNotThePasswordYouAreL00kingFor",
|
||||
CONF_API_TOKEN: "ThisIsNotThePasswordYouAreL00kingFor",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: True,
|
||||
},
|
||||
|
||||
@@ -16,11 +16,10 @@ from homeassistant.components.traccar_server.const import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
CONF_API_TOKEN,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_USERNAME,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -44,8 +43,7 @@ async def test_form(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
@@ -55,8 +53,7 @@ async def test_form(
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: "8082",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: True,
|
||||
}
|
||||
@@ -87,8 +84,7 @@ async def test_form_cannot_connect(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -101,8 +97,7 @@ async def test_form_cannot_connect(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
@@ -112,8 +107,7 @@ async def test_form_cannot_connect(
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: "8082",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: True,
|
||||
}
|
||||
@@ -168,8 +162,7 @@ async def test_abort_already_configured(
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: "8082",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_API_TOKEN: "test-token",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -201,8 +194,7 @@ async def test_reauth_flow(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "new-username",
|
||||
CONF_PASSWORD: "new-password",
|
||||
CONF_API_TOKEN: "new-token",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
@@ -211,8 +203,7 @@ async def test_reauth_flow(
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
# Verify the config entry was updated
|
||||
assert mock_config_entry.data[CONF_USERNAME] == "new-username"
|
||||
assert mock_config_entry.data[CONF_PASSWORD] == "new-password"
|
||||
assert mock_config_entry.data[CONF_API_TOKEN] == "new-token"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -248,8 +239,7 @@ async def test_reauth_flow_errors(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "new-username",
|
||||
CONF_PASSWORD: "new-password",
|
||||
CONF_API_TOKEN: "new-token",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -262,8 +252,7 @@ async def test_reauth_flow_errors(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "new-username",
|
||||
CONF_PASSWORD: "new-password",
|
||||
CONF_API_TOKEN: "new-token",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
Reference in New Issue
Block a user