Идемпотентность особенно важна для сетевых операций. Обычно устройство отправляет запрос и получает ответ. Но сети ненадежны. Конечно, отправка запроса может завершиться ошибкой. Но также может случиться так, что вы отправляете запрос, он успешно обрабатывается на сервере, но потом теряется сетевое соединение и вы так и не получаете ответ. Идемпотентность означает, что такие операции можно безопасно повторять, что не имеет значения, выдается ли запрос 1 или 10 раз. Это проще, когда вы выражаете эти операции не как изменение состояния или действие, а как желаемое состояние, которого вы хотите достичь.
Здесь имеет смысл сделать так, чтобы сетевой запрос достиг состояния «устройство с этим кодом погашения привязано к этому паролю». Это позволяет изначально установить новый пароль и позволяет устройству убедиться, что оно правильно зарегистрировано, но не позволяет устройству изменить пароль позже.
Затем этот фрагмент кода может сначала безопасно создать и сохранить пароль, а затем всегда повторять сетевой запрос:
template<class T> T load_or_default(StorageLocation, T default);
template<class T> void store_or_fail(StorageLocation, T value);
// 1. ensure that we have created a password
auto password = load_or_default(PASSWORD, "");
if (!password) {
password = generate_password();
store_or_fail(PASSWORD, password);
}
// 2. ensure that we are registered
ensure_registration_with_server_or_fail(password, redeem_code);
В качестве оптимизации вы можете сохранить дополнительную переменную, которая представляет статус регистрации:
// 2. ensure that we are registered
auto is_registered = load_or_default(IS_REGISTERED, false);
if (!is_registered) {
ensure_registration_with_server_or_fail(password, redeem_code);
store_or_fail(IS_REGISTERED, true);
}
Концепция идемпотентности особенно важна в протоколе HTTP. Согласно анализу Роя Филдинга, этот протокол хорошо подходит для передачи состояния (REST API). В HTTP некоторые методы, такие как GET и PUT, считаются идемпотентными, а POST — нет. Ваша регистрация может быть элегантно выражена с помощью операции в стиле PUT.
Чтобы завершить смену пароля, устройство может войти в систему с новым паролем, и только после этого старый пароль становится недействительным.
Если устройство входит в систему со старым паролем, оно проходит, но процедура смены пароля считается неудачной. В этот момент смена пароля может быть повторена.
Также необходимо иметь два файла для пароля на флэш-памяти устройства. Более новый используется при условии, что он соответствует (правильный размер, возможно, контрольная сумма). При обнаружении несоответствия он может быть удален, и тогда используется более старый. Новый пароль перезаписывает старый файл.
В течение короткого времени два пароля действительны, но если они достаточно сложны, я не считаю это большим нарушением безопасности.
Можете ли вы сохранить пароль на флэш-памяти, затем отправить пароль на сервер, а затем сохранить на флэш-памяти заметку о том, что пароль был загружен? Если устройство видит пароль на флэш-памяти, но не видит заметки, то оно должно попытаться повторно загрузить пароль, затем, если загрузка прошла успешно или если она не удалась, потому что пароль уже есть, сохранить заметку на флэш-памяти.