master
cpp 219 lines 9 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCE2ERegistryTests.cpp
8
9 Abstract:
10
11 End-to-end tests for wslc registry login/logout auth flows against a local registry.
12
13 --*/
14
15 #include "precomp.h"
16 #include "windows/Common.h"
17 #include "WSLCExecutor.h"
18 #include "WSLCE2EHelpers.h"
19 #include "TestImageRegistry.h"
20 #include "Argument.h"
21 #include <wslutil.h>
22
23 namespace WSLCE2ETests {
24 using namespace wsl::shared;
25 using namespace WEX::Logging;
26
27 namespace {
28
29 constexpr auto c_username = "wslctest";
30 constexpr auto c_password = "password";
31
32 void VerifyAuthFailure(const WSLCExecutionResult& result)
33 {
34 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
35 VERIFY_IS_TRUE(result.Stderr.has_value());
36 VERIFY_IS_TRUE(result.Stderr->find(L"no basic auth credentials") != std::wstring::npos);
37 }
38
39 void VerifyLogoutSucceeds(const std::wstring& registryAddress)
40 {
41 auto result = RunWslc(std::format(L"logout {}", registryAddress));
42 result.Verify({.Stdout = Localization::WSLCCLI_LogoutSucceeded(registryAddress) + L"\r\n", .Stderr = L"", .ExitCode = 0});
43 }
44 } // namespace
45
46 class WSLCE2ERegistryTests
47 {
48 WSLC_TEST_CLASS(WSLCE2ERegistryTests)
49
50 WSLC_TEST_METHOD(WSLCE2E_Registry_LoginLogout_PushPull_AuthFlow)
51 {
52 const auto& testImage = AlpineTestImage();
53 TestImageRegistry::Instance().EnsureLoaded(testImage);
54
55 auto session = OpenDefaultElevatedSession();
56
57 {
58 auto [registryContainer, registryAddress] = StartLocalRegistry(*session, c_username, c_password, 15001);
59 auto registryAddressW = string::MultiByteToWide(registryAddress);
60
61 auto registryImageName = TagImageForRegistry(testImage.NameAndTag(), registryAddressW);
62
63 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
64 RunWslc(std::format(L"image delete --force {}", registryImageName));
65 RunWslc(std::format(L"logout {}", registryAddressW));
66 });
67
68 // Negative path before login: push and pull should fail.
69 auto result = RunWslc(std::format(L"push {}", registryImageName));
70 VerifyAuthFailure(result);
71
72 RunWslcAndVerify(std::format(L"image delete --force {}", registryImageName), {.ExitCode = 0});
73
74 result = RunWslc(std::format(L"pull {}", registryImageName));
75 VerifyAuthFailure(result);
76
77 // Login and verify that saved credentials are used for push/pull.
78 result = RunWslc(std::format(
79 L"login -u {} -p {} {}", string::MultiByteToWide(c_username), string::MultiByteToWide(c_password), registryAddressW));
80 result.Verify({.Stdout = Localization::WSLCCLI_LoginSucceeded() + L"\r\n", .Stderr = L"", .ExitCode = 0});
81
82 registryImageName = TagImageForRegistry(testImage.NameAndTag(), registryAddressW);
83 result = RunWslc(std::format(L"push {}", registryImageName));
84 result.Verify({.ExitCode = 0});
85
86 RunWslcAndVerify(std::format(L"image delete --force {}", registryImageName), {.ExitCode = 0});
87 result = RunWslc(std::format(L"pull {}", registryImageName));
88 result.Verify({.Stderr = L"", .ExitCode = 0});
89
90 // Logout and verify both pull and push fail again.
91 VerifyLogoutSucceeds(registryAddressW);
92
93 RunWslcAndVerify(std::format(L"image delete --force {}", registryImageName), {.ExitCode = 0});
94 result = RunWslc(std::format(L"pull {}", registryImageName));
95 VerifyAuthFailure(result);
96
97 registryImageName = TagImageForRegistry(testImage.NameAndTag(), registryAddressW);
98 result = RunWslc(std::format(L"push {}", registryImageName));
99 VerifyAuthFailure(result);
100
101 // Negative path for logout command: second logout should fail.
102 result = RunWslc(std::format(L"logout {}", registryAddressW));
103 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
104 VERIFY_IS_TRUE(result.Stderr.has_value());
105 VERIFY_IS_TRUE(result.Stderr->find(L"Not logged in to") != std::wstring::npos);
106 }
107 }
108
109 WSLC_TEST_METHOD(WSLCE2E_Registry_Login_HelpCommand)
110 {
111 auto result = RunWslc(L"registry login --help");
112 result.Verify({.Stderr = L"", .ExitCode = 0});
113 VERIFY_IS_FALSE(result.Stdout.value().empty());
114 }
115
116 WSLC_TEST_METHOD(WSLCE2E_Registry_Logout_HelpCommand)
117 {
118 auto result = RunWslc(L"registry logout --help");
119 result.Verify({.Stderr = L"", .ExitCode = 0});
120 VERIFY_IS_FALSE(result.Stdout.value().empty());
121 }
122
123 WSLC_TEST_METHOD(WSLCE2E_Registry_Login_PasswordAndStdinMutuallyExclusive)
124 {
125 auto result = RunWslc(L"login -u testuser -p testpass --password-stdin localhost:15099");
126 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
127 VERIFY_IS_TRUE(result.Stderr.has_value());
128 VERIFY_IS_TRUE(result.Stderr->find(L"--password and --password-stdin are mutually exclusive") != std::wstring::npos);
129 }
130
131 WSLC_TEST_METHOD(WSLCE2E_Registry_Login_PasswordStdinRequiresUsername)
132 {
133 auto result = RunWslc(L"login --password-stdin localhost:15099");
134 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
135 VERIFY_IS_TRUE(result.Stderr.has_value());
136 VERIFY_IS_TRUE(result.Stderr->find(L"Must provide --username with --password-stdin") != std::wstring::npos);
137 }
138
139 WSLC_TEST_METHOD(WSLCE2E_Registry_Login_InvalidCredentials)
140 {
141 auto session = OpenDefaultElevatedSession();
142
143 {
144 auto [registryContainer, registryAddress] = StartLocalRegistry(*session, c_username, c_password, 15003);
145 auto registryAddressW = string::MultiByteToWide(registryAddress);
146
147 // Login with wrong password should fail.
148 {
149 auto result =
150 RunWslc(std::format(L"login -u {} -p wrongpassword {}", string::MultiByteToWide(c_username), registryAddressW));
151 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
152 VERIFY_IS_TRUE(result.Stderr.has_value());
153 VERIFY_IS_TRUE(result.Stderr->find(L"401 Unauthorized") != std::wstring::npos);
154 }
155
156 // Login with wrong username should fail.
157 {
158 auto result = RunWslc(std::format(L"login -u wronguser -p {} {}", string::MultiByteToWide(c_password), registryAddressW));
159 VERIFY_ARE_EQUAL(1u, result.ExitCode.value_or(0));
160 VERIFY_IS_TRUE(result.Stderr.has_value());
161 VERIFY_IS_TRUE(result.Stderr->find(L"401 Unauthorized") != std::wstring::npos);
162 }
163
164 // Login with correct credentials should still succeed after failed attempts.
165 {
166 auto result = RunWslc(std::format(
167 L"login -u {} -p {} {}", string::MultiByteToWide(c_username), string::MultiByteToWide(c_password), registryAddressW));
168 result.Verify({.Stdout = Localization::WSLCCLI_LoginSucceeded() + L"\r\n", .Stderr = L"", .ExitCode = 0});
169
170 VerifyLogoutSucceeds(registryAddressW);
171 }
172 }
173 }
174
175 WSLC_TEST_METHOD(WSLCE2E_Registry_Login_CredentialInputMethods)
176 {
177 auto session = OpenDefaultElevatedSession();
178
179 {
180 auto [registryContainer, registryAddress] = StartLocalRegistry(*session, c_username, c_password, 15002);
181 auto registryAddressW = string::MultiByteToWide(registryAddress);
182 auto usernameW = string::MultiByteToWide(c_username);
183 auto passwordW = string::MultiByteToWide(c_password);
184
185 // Login with -u and -p flags.
186 {
187 auto result = RunWslc(std::format(L"login -u {} -p {} {}", usernameW, passwordW, registryAddressW));
188 result.Verify({.Stdout = Localization::WSLCCLI_LoginSucceeded() + L"\r\n", .Stderr = L"", .ExitCode = 0});
189
190 VerifyLogoutSucceeds(registryAddressW);
191 }
192
193 // Login with -u and --password-stdin.
194 {
195 auto interactive = RunWslcInteractive(std::format(L"login -u {} --password-stdin {}", usernameW, registryAddressW));
196 interactive.WriteLine(c_password);
197 interactive.CloseStdin();
198 auto exitCode = interactive.Wait();
199 VERIFY_ARE_EQUAL(0, exitCode, L"Login with --password-stdin should succeed");
200
201 VerifyLogoutSucceeds(registryAddressW);
202 }
203
204 // Login with interactive prompts (no flags).
205 {
206 auto interactive = RunWslcInteractive(std::format(L"login {}", registryAddressW));
207 interactive.ExpectStdout("Username: ");
208 interactive.WriteLine(c_username);
209 interactive.ExpectStdout("Password: ");
210 interactive.WriteLine(c_password);
211 auto exitCode = interactive.Wait();
212 VERIFY_ARE_EQUAL(0, exitCode, L"Interactive login should succeed");
213
214 VerifyLogoutSucceeds(registryAddressW);
215 }
216 }
217 }
218 };
219 } // namespace WSLCE2ETests