SHA256
23 01 25
Сделал авторификацию новую через sessionKey (Но пока тесты сессии падают)
This commit is contained in:
@@ -13,11 +13,16 @@ import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* IT_02_Sessions
|
||||
* IT_02_Sessions (v2)
|
||||
*
|
||||
* Цель:
|
||||
* - проверить создание/листинг/refresh/close
|
||||
* - проверить создание/листинг/вход-в-сессию(2 шага)/close
|
||||
* - и после завершения оставить в БД 3 активных сессии (S1,S2,S3)
|
||||
*
|
||||
* Протокол v2:
|
||||
* - создание сессии: AuthChallenge -> CreateAuthSession (deviceKey подпись, + sessionPubKey)
|
||||
* - вход в сессию: SessionChallenge(sessionId) -> nonce, затем SessionLogin(sessionId,time,signature(sessionKey))
|
||||
* - ListSessions и CloseActiveSession доступны только в AUTH_STATUS_USER (после SessionLogin)
|
||||
*/
|
||||
public class IT_02_Sessions {
|
||||
|
||||
@@ -31,110 +36,65 @@ public class IT_02_Sessions {
|
||||
}
|
||||
|
||||
public static String run() {
|
||||
TestResult r = new TestResult("IT_02_Sessions");
|
||||
TestResult r = new TestResult("IT_02_Sessions(v2)");
|
||||
|
||||
Duration t = Duration.ofSeconds(5);
|
||||
|
||||
String s1Id, s1Pwd;
|
||||
String s2Id, s2Pwd;
|
||||
String s3Id, s3Pwd;
|
||||
Session s1, s2, s3;
|
||||
|
||||
try {
|
||||
// 1) Создаём 3 сессии (каждая — отдельным соединением, чтобы не зависеть от состояния WS)
|
||||
Session s1 = createSession(LOGIN, t, r, "S1");
|
||||
s1Id = s1.sessionId; s1Pwd = s1.sessionPwd;
|
||||
// 1) Создаём 3 сессии (каждая — отдельным соединением)
|
||||
s1 = createSession(LOGIN, t, r, "S1");
|
||||
s2 = createSession(LOGIN, t, r, "S2");
|
||||
s3 = createSession(LOGIN, t, r, "S3");
|
||||
|
||||
Session s2 = createSession(LOGIN, t, r, "S2");
|
||||
s2Id = s2.sessionId; s2Pwd = s2.sessionPwd;
|
||||
|
||||
Session s3 = createSession(LOGIN, t, r, "S3");
|
||||
s3Id = s3.sessionId; s3Pwd = s3.sessionPwd;
|
||||
|
||||
// 2) ListSessions в AUTH_IN_PROGRESS — должны быть S1,S2,S3
|
||||
// 2) Входим в S1 (2 шага) и делаем ListSessions (AUTH_STATUS_USER) — должны быть S1,S2,S3
|
||||
try (WsSession ws = WsSession.open()) {
|
||||
String nonceResp = ws.call("AuthChallenge(list)", JsonBuilders.authChallenge(LOGIN), t);
|
||||
assertEquals(200, JsonParsers.status(nonceResp), "AuthChallenge(list) must be 200");
|
||||
String nonce = JsonParsers.authNonce(nonceResp);
|
||||
assertNotNull(nonce, "authNonce must not be null");
|
||||
sessionLogin2Steps(ws, s1, t, "Login(S1)", r);
|
||||
|
||||
long timeMs = System.currentTimeMillis();
|
||||
String sig = JsonBuilders.signAuthorificated(nonce, timeMs, TestConfig.getDevicePrivatKey(LOGIN));
|
||||
|
||||
String listResp = ws.call("ListSessions(AUTH_IN_PROGRESS)", JsonBuilders.listSessions(timeMs, sig), t);
|
||||
assertEquals(200, JsonParsers.status(listResp), "ListSessions must be 200");
|
||||
String listResp = ws.call("ListSessions(AUTH_STATUS_USER)", JsonBuilders.listSessions(0L, ""), t);
|
||||
assertEquals(200, JsonParsers.status(listResp), "ListSessions(AUTH_STATUS_USER) must be 200");
|
||||
|
||||
List<String> ids = JsonParsers.sessionIds(listResp);
|
||||
r.ok("ListSessions(AUTH_IN_PROGRESS): " + ids);
|
||||
r.ok("ListSessions(AUTH_STATUS_USER): " + ids);
|
||||
|
||||
assertTrue(ids.contains(s1Id), "Must contain S1");
|
||||
assertTrue(ids.contains(s2Id), "Must contain S2");
|
||||
assertTrue(ids.contains(s3Id), "Must contain S3");
|
||||
assertTrue(ids.contains(s1.sessionId), "Must contain S1");
|
||||
assertTrue(ids.contains(s2.sessionId), "Must contain S2");
|
||||
assertTrue(ids.contains(s3.sessionId), "Must contain S3");
|
||||
r.ok("Проверка OK: список содержит S1,S2,S3");
|
||||
}
|
||||
|
||||
// 3) RefreshSession(S1) -> после refresh в этом же соединении делаем ListSessions(AUTH_STATUS_USER) (timeMs=0)
|
||||
try (WsSession ws = WsSession.open()) {
|
||||
String refreshResp = ws.call("RefreshSession(S1)", JsonBuilders.refreshSession(s1Id, s1Pwd), t);
|
||||
assertEquals(200, JsonParsers.status(refreshResp), "RefreshSession(S1) must be 200");
|
||||
assertNotNull(JsonParsers.storagePwd(refreshResp), "storagePwd must not be null");
|
||||
r.ok("RefreshSession(S1): OK");
|
||||
|
||||
String listInUserResp = ws.call("ListSessions(AUTH_STATUS_USER)", JsonBuilders.listSessions(0L, ""), t);
|
||||
assertEquals(200, JsonParsers.status(listInUserResp), "ListSessions(AUTH_STATUS_USER) must be 200");
|
||||
|
||||
List<String> ids = JsonParsers.sessionIds(listInUserResp);
|
||||
r.ok("ListSessions(AUTH_STATUS_USER): " + ids);
|
||||
|
||||
assertTrue(ids.contains(s1Id));
|
||||
assertTrue(ids.contains(s2Id));
|
||||
assertTrue(ids.contains(s3Id));
|
||||
r.ok("Проверка OK: AUTH_STATUS_USER список содержит S1,S2,S3");
|
||||
}
|
||||
|
||||
// 4) Проверяем CloseActiveSession, но так, чтобы итогом всё равно осталось 3 сессии:
|
||||
// создаём TEMP, закрываем TEMP, убеждаемся что S1,S2,S3 остались.
|
||||
// 3) Проверяем CloseActiveSession так, чтобы итогом всё равно осталось 3 сессии:
|
||||
// создаём TEMP, логинимся в S1, закрываем TEMP, убеждаемся что S1,S2,S3 остались.
|
||||
Session temp = createSession(LOGIN, t, r, "TEMP");
|
||||
String tempId = temp.sessionId;
|
||||
|
||||
try (WsSession ws = WsSession.open()) {
|
||||
String nonceResp = ws.call("AuthChallenge(close TEMP)", JsonBuilders.authChallenge(LOGIN), t);
|
||||
assertEquals(200, JsonParsers.status(nonceResp), "AuthChallenge(close TEMP) must be 200");
|
||||
String nonce = JsonParsers.authNonce(nonceResp);
|
||||
assertNotNull(nonce);
|
||||
sessionLogin2Steps(ws, s1, t, "Login(S1) for close", r);
|
||||
|
||||
long timeMs = System.currentTimeMillis();
|
||||
String sig = JsonBuilders.signAuthorificated(nonce, timeMs, TestConfig.getDevicePrivatKey(LOGIN));
|
||||
|
||||
String closeResp = ws.call("CloseActiveSession(TEMP)", JsonBuilders.closeActiveSession(tempId, timeMs, sig), t);
|
||||
String closeResp = ws.call("CloseActiveSession(TEMP)", JsonBuilders.closeActiveSession(temp.sessionId, 0L, ""), t);
|
||||
assertEquals(200, JsonParsers.status(closeResp), "CloseActiveSession(TEMP) must be 200");
|
||||
r.ok("CloseActiveSession(TEMP): OK");
|
||||
}
|
||||
|
||||
// 5) Финальная проверка: снова ListSessions(AUTH_IN_PROGRESS) => S1,S2,S3 должны остаться, TEMP нет
|
||||
// 4) Финальная проверка: снова логинимся в S1 и ListSessions => S1,S2,S3 должны остаться, TEMP нет
|
||||
try (WsSession ws = WsSession.open()) {
|
||||
String nonceResp = ws.call("AuthChallenge(final list)", JsonBuilders.authChallenge(LOGIN), t);
|
||||
assertEquals(200, JsonParsers.status(nonceResp));
|
||||
String nonce = JsonParsers.authNonce(nonceResp);
|
||||
assertNotNull(nonce);
|
||||
sessionLogin2Steps(ws, s1, t, "Final Login(S1)", r);
|
||||
|
||||
long timeMs = System.currentTimeMillis();
|
||||
String sig = JsonBuilders.signAuthorificated(nonce, timeMs, TestConfig.getDevicePrivatKey(LOGIN));
|
||||
|
||||
String listResp = ws.call("ListSessions(final AUTH_IN_PROGRESS)", JsonBuilders.listSessions(timeMs, sig), t);
|
||||
String listResp = ws.call("ListSessions(final)", JsonBuilders.listSessions(0L, ""), t);
|
||||
assertEquals(200, JsonParsers.status(listResp));
|
||||
|
||||
List<String> ids = JsonParsers.sessionIds(listResp);
|
||||
r.ok("Final ListSessions: " + ids);
|
||||
|
||||
assertTrue(ids.contains(s1Id));
|
||||
assertTrue(ids.contains(s2Id));
|
||||
assertTrue(ids.contains(s3Id));
|
||||
assertFalse(ids.contains(tempId));
|
||||
assertTrue(ids.contains(s1.sessionId));
|
||||
assertTrue(ids.contains(s2.sessionId));
|
||||
assertTrue(ids.contains(s3.sessionId));
|
||||
assertFalse(ids.contains(temp.sessionId));
|
||||
r.ok("ИТОГ OK: после теста в БД остались 3 активные сессии (S1,S2,S3)");
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
r.fail("IT_02_Sessions упал: " + e.getMessage());
|
||||
r.fail("IT_02_Sessions(v2) упал: " + e.getMessage());
|
||||
}
|
||||
|
||||
return r.summaryLine();
|
||||
@@ -142,24 +102,56 @@ public class IT_02_Sessions {
|
||||
|
||||
private static Session createSession(String login, Duration t, TestResult r, String label) {
|
||||
try (WsSession ws = WsSession.open()) {
|
||||
|
||||
// шаг 1: AuthChallenge
|
||||
String nonceResp = ws.call("AuthChallenge(" + label + ")", JsonBuilders.authChallenge(login), t);
|
||||
assertEquals(200, JsonParsers.status(nonceResp), "AuthChallenge(" + label + ") must be 200");
|
||||
String nonce = JsonParsers.authNonce(nonceResp);
|
||||
assertNotNull(nonce, "authNonce must not be null for " + label);
|
||||
String authNonce = JsonParsers.authNonce(nonceResp);
|
||||
assertNotNull(authNonce, "authNonce must not be null for " + label);
|
||||
|
||||
String createResp = ws.call("CreateAuthSession(" + label + ")", JsonBuilders.createAuthSession(login, nonce, TestConfig.fakeStoragePwd()), t);
|
||||
// для тестов: sessionKey = deviceKey (в реале будет отдельный keypair)
|
||||
String sessionPubKeyB64 = TestConfig.devicePublicKeyB64(login);
|
||||
|
||||
// storagePwd на клиенте (сохраняем, чтобы потом проверить, что сервер вернул именно его)
|
||||
String storagePwd = TestConfig.fakeStoragePwd();
|
||||
|
||||
// шаг 2: CreateAuthSession (device подпись + sessionPubKey)
|
||||
String createResp = ws.call(
|
||||
"CreateAuthSession(" + label + ")",
|
||||
JsonBuilders.createAuthSessionV2(login, authNonce, storagePwd, sessionPubKeyB64),
|
||||
t
|
||||
);
|
||||
assertEquals(200, JsonParsers.status(createResp), "CreateAuthSession(" + label + ") must be 200");
|
||||
|
||||
String sid = JsonParsers.sessionId(createResp);
|
||||
String spw = JsonParsers.sessionPwd(createResp);
|
||||
|
||||
assertNotNull(sid, "sessionId must not be null");
|
||||
assertNotNull(spw, "sessionPwd must not be null");
|
||||
|
||||
r.ok("Создана сессия " + label + ": sessionId=" + sid);
|
||||
return new Session(sid, spw);
|
||||
|
||||
// для тестов используем devicePriv как sessionPriv
|
||||
byte[] sessionPrivKey = TestConfig.getDevicePrivatKey(login);
|
||||
|
||||
return new Session(sid, sessionPrivKey, storagePwd);
|
||||
}
|
||||
}
|
||||
|
||||
private record Session(String sessionId, String sessionPwd) {}
|
||||
private static void sessionLogin2Steps(WsSession ws, Session s, Duration t, String label, TestResult r) {
|
||||
// шаг 1: SessionChallenge(sessionId)
|
||||
String chResp = ws.call("SessionChallenge " + label, JsonBuilders.sessionChallenge(s.sessionId), t);
|
||||
assertEquals(200, JsonParsers.status(chResp), "SessionChallenge must be 200");
|
||||
String nonce = JsonParsers.sessionNonce(chResp);
|
||||
assertNotNull(nonce, "SessionChallenge nonce must not be null");
|
||||
|
||||
// шаг 2: SessionLogin(sessionId, timeMs, signature(sessionKey, SESSION_LOGIN:...))
|
||||
String loginResp = ws.call("SessionLogin " + label, JsonBuilders.sessionLogin(s.sessionId, nonce, s.sessionPrivKey), t);
|
||||
assertEquals(200, JsonParsers.status(loginResp), "SessionLogin must be 200");
|
||||
|
||||
String storagePwd = JsonParsers.storagePwd(loginResp);
|
||||
assertNotNull(storagePwd, "storagePwd must not be null after SessionLogin");
|
||||
assertEquals(s.storagePwd, storagePwd, "storagePwd must match what client provided on CreateAuthSession");
|
||||
|
||||
r.ok(label + ": SessionLogin OK, storagePwd verified");
|
||||
}
|
||||
|
||||
private record Session(String sessionId, byte[] sessionPrivKey, String storagePwd) {}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import test.it.cases.IT_01_AddUser;
|
||||
import test.it.cases.IT_02_Sessions;
|
||||
import test.it.cases.IT_03_AddBlock_NoAuth;
|
||||
import test.it.cases.IT_04_UserParams_NoAuth;
|
||||
import test.it.cases.IT_05_ListSubscribedChannels_200;
|
||||
import test.it.utils.log.TestLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -31,7 +30,6 @@ public class IT_RunAllMain {
|
||||
String s2 = IT_02_Sessions.run(); summaries.add(s2); if (s2.contains("FAIL:")) failed++;
|
||||
String s3 = IT_03_AddBlock_NoAuth.run(); summaries.add(s3); if (s3.contains("FAIL:")) failed++;
|
||||
String s4 = IT_04_UserParams_NoAuth.run(); summaries.add(s4); if (s4.contains("FAIL:")) failed++;
|
||||
String s5 = IT_05_ListSubscribedChannels_200.run(); summaries.add(s5); if (s5.contains("FAIL:")) failed++;
|
||||
|
||||
TestLog.title("IT RUN RESULT (per test)");
|
||||
for (String s : summaries) System.out.println(s);
|
||||
|
||||
@@ -58,9 +58,11 @@ public final class JsonBuilders {
|
||||
""".formatted(requestId, login);
|
||||
}
|
||||
|
||||
// ---------------- CreateAuthSession ----------------
|
||||
// ---------------- CreateAuthSession (v2) ----------------
|
||||
// v2: sessionKey генерируется на клиенте, на сервер отправляем только sessionPubKey (base64).
|
||||
// подпись шага CreateAuthSession всё ещё делается deviceKey: "AUTHORIFICATED:" + timeMs + authNonce
|
||||
|
||||
public static String createAuthSession(String login, String authNonce, String storagePwd) {
|
||||
public static String createAuthSessionV2(String login, String authNonce, String storagePwd, String sessionPubKeyB64) {
|
||||
long timeMs = System.currentTimeMillis();
|
||||
byte[] devicePriv = TestConfig.getDevicePrivatKey(login);
|
||||
String sigB64 = signAuthorificated(authNonce, timeMs, devicePriv);
|
||||
@@ -72,12 +74,56 @@ public final class JsonBuilders {
|
||||
"requestId": "%s",
|
||||
"payload": {
|
||||
"storagePwd": "%s",
|
||||
"sessionPubKeyB64": "%s",
|
||||
"timeMs": %d,
|
||||
"signatureB64": "%s",
|
||||
"clientInfo": "%s"
|
||||
}
|
||||
}
|
||||
""".formatted(requestId, storagePwd, timeMs, sigB64, TestConfig.TEST_CLIENT_INFO);
|
||||
""".formatted(
|
||||
requestId,
|
||||
storagePwd,
|
||||
sessionPubKeyB64,
|
||||
timeMs,
|
||||
sigB64,
|
||||
TestConfig.TEST_CLIENT_INFO
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------- SessionChallenge (v2) ----------------
|
||||
|
||||
public static String sessionChallenge(String sessionId) {
|
||||
String requestId = TestIds.next("sch");
|
||||
return """
|
||||
{
|
||||
"op": "SessionChallenge",
|
||||
"requestId": "%s",
|
||||
"payload": {
|
||||
"sessionId": "%s"
|
||||
}
|
||||
}
|
||||
""".formatted(requestId, sessionId);
|
||||
}
|
||||
|
||||
// ---------------- SessionLogin (v2) ----------------
|
||||
|
||||
public static String sessionLogin(String sessionId, String nonce, byte[] sessionPrivKey) {
|
||||
long timeMs = System.currentTimeMillis();
|
||||
String sigB64 = signSessionLogin(sessionId, timeMs, nonce, sessionPrivKey);
|
||||
|
||||
String requestId = TestIds.next("slogin");
|
||||
return """
|
||||
{
|
||||
"op": "SessionLogin",
|
||||
"requestId": "%s",
|
||||
"payload": {
|
||||
"sessionId": "%s",
|
||||
"timeMs": %d,
|
||||
"signatureB64": "%s",
|
||||
"clientInfo": "%s"
|
||||
}
|
||||
}
|
||||
""".formatted(requestId, sessionId, timeMs, sigB64, TestConfig.TEST_CLIENT_INFO);
|
||||
}
|
||||
|
||||
// ---------------- ListSessions ----------------
|
||||
@@ -97,23 +143,6 @@ public final class JsonBuilders {
|
||||
""".formatted(requestId, timeMs, signatureB64);
|
||||
}
|
||||
|
||||
// ---------------- RefreshSession ----------------
|
||||
|
||||
public static String refreshSession(String sessionId, String sessionPwd) {
|
||||
String requestId = TestIds.next("refresh");
|
||||
return """
|
||||
{
|
||||
"op": "RefreshSession",
|
||||
"requestId": "%s",
|
||||
"payload": {
|
||||
"sessionId": "%s",
|
||||
"sessionPwd": "%s",
|
||||
"clientInfo": "%s"
|
||||
}
|
||||
}
|
||||
""".formatted(requestId, sessionId, sessionPwd, TestConfig.TEST_CLIENT_INFO);
|
||||
}
|
||||
|
||||
// ---------------- CloseActiveSession ----------------
|
||||
|
||||
public static String closeActiveSession(String sessionId, long timeMs, String signatureB64) {
|
||||
@@ -145,7 +174,6 @@ public final class JsonBuilders {
|
||||
""".formatted(requestId, login);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Подпись для режима AUTH_IN_PROGRESS:
|
||||
* preimage = "AUTHORIFICATED:" + timeMs + authNonce
|
||||
@@ -157,4 +185,16 @@ public final class JsonBuilders {
|
||||
byte[] sig = Ed25519Util.sign(preimage, devicePrivKey);
|
||||
return Base64.getEncoder().encodeToString(sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Подпись для SessionLogin(v2):
|
||||
* preimage = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce
|
||||
* подписываем sessionPrivKey.
|
||||
*/
|
||||
public static String signSessionLogin(String sessionId, long timeMs, String nonce, byte[] sessionPrivKey) {
|
||||
String preimageStr = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce;
|
||||
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] sig = Ed25519Util.sign(preimage, sessionPrivKey);
|
||||
return Base64.getEncoder().encodeToString(sig);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,18 @@ public final class JsonParsers {
|
||||
}
|
||||
}
|
||||
|
||||
/** nonce из SessionChallenge(v2) */
|
||||
public static String sessionNonce(String json) {
|
||||
try {
|
||||
JsonNode root = MAPPER.readTree(json);
|
||||
JsonNode payload = root.get("payload");
|
||||
if (payload != null && payload.has("nonce")) return payload.get("nonce").asText();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String sessionId(String json) {
|
||||
try {
|
||||
JsonNode root = MAPPER.readTree(json);
|
||||
@@ -41,6 +53,7 @@ public final class JsonParsers {
|
||||
}
|
||||
}
|
||||
|
||||
// оставляю для совместимости с другими тестами, но в IT_02(v2) больше не используется
|
||||
public static String sessionPwd(String json) {
|
||||
try {
|
||||
JsonNode root = MAPPER.readTree(json);
|
||||
|
||||
Reference in New Issue
Block a user