<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cloudpods –
Authentication</title><link>/docs/en/tags/authentication/</link><description>Recent content in Authentication on Cloudpods</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><atom:link href="/docs/en/tags/authentication/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: Login Test Cases</title><link>/docs/en/docs/web_ui/testcase/login/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/docs/en/docs/web_ui/testcase/login/</guid><description>
&lt;h2 id="scope"&gt;Scope&lt;/h2&gt;
&lt;p&gt;Covers login, login security policies, logout, and session management of the OneCloud Web console. See the following product features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="../../../practice/login"&gt;Login page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../intro/usercenter/userinfor"&gt;User information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../intro/usercenter/signout"&gt;Sign out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../intro/ui/view"&gt;Switch view&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../auth-security/authentication/authentication-system/systemuser"&gt;User management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../auth-security/security/security-alert/securityalert"&gt;Security alert&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="environment-and-account"&gt;Environment and account&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Address&lt;/td&gt;
&lt;td&gt;&lt;a href="https://test.yunion.io" target="_blank" rel="noopener noreferrer"&gt;https://test.yunion.io&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account&lt;/td&gt;
&lt;td&gt;cloudadmin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;cloudadmin@test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Login page elements (the actual UI prevails): the Account login / Mobile number login tabs, username input box, password input box, login button, image captcha (shown according to policy), Specify login domain entry, Forgot password, and third-party/CAS login entry.&lt;/p&gt;
&lt;h2 id="case-overview"&gt;Case overview&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;Case name&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-001&lt;/td&gt;
&lt;td&gt;Log in with the correct username and password&lt;/td&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-002&lt;/td&gt;
&lt;td&gt;Log in with a wrong password&lt;/td&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-003&lt;/td&gt;
&lt;td&gt;Log in with a non-existent username&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-004&lt;/td&gt;
&lt;td&gt;Submit with an empty username or password&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-005&lt;/td&gt;
&lt;td&gt;Log in with a specified login domain&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-006&lt;/td&gt;
&lt;td&gt;Login fails when the domain and user do not match&lt;/td&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-007&lt;/td&gt;
&lt;td&gt;An image captcha appears after repeated wrong passwords&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-008&lt;/td&gt;
&lt;td&gt;Exceeding the wrong-password limit locks the account and generates a security alert&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-009&lt;/td&gt;
&lt;td&gt;A user with &amp;ldquo;Console login&amp;rdquo; disabled cannot log in&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-010&lt;/td&gt;
&lt;td&gt;A disabled user cannot log in&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-011&lt;/td&gt;
&lt;td&gt;Log in as a user with MFA enabled&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-012&lt;/td&gt;
&lt;td&gt;A user with an expired password cannot log in&lt;/td&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-013&lt;/td&gt;
&lt;td&gt;The landing view after login is correct&lt;/td&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-014&lt;/td&gt;
&lt;td&gt;The session becomes invalid after logout&lt;/td&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-015&lt;/td&gt;
&lt;td&gt;Accessing an inner page without logging in redirects to the login page&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-016&lt;/td&gt;
&lt;td&gt;Switch the language of the login page&lt;/td&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-017&lt;/td&gt;
&lt;td&gt;Log in as an identity provider (LDAP) user&lt;/td&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TC-LOGIN-018&lt;/td&gt;
&lt;td&gt;Login session validity period&lt;/td&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-001-log-in-with-the-correct-username-and-password"&gt;TC-LOGIN-001 Log in with the correct username and password&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P0&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: The cloudadmin account is enabled and allowed to log in to the console.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://test.yunion.io" target="_blank" rel="noopener noreferrer"&gt;https://test.yunion.io&lt;/a&gt; in a browser.&lt;/li&gt;
&lt;li&gt;On the &amp;ldquo;Account login&amp;rdquo; tab, enter the username &lt;code&gt;cloudadmin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter the password &lt;code&gt;cloudadmin@test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After submission, the UI shows a transitional state such as &amp;ldquo;Logging in, please wait&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Login succeeds and jumps to the dashboard page, with the current user shown in the upper right corner.&lt;/li&gt;
&lt;li&gt;The top area shows the view switch entry, and the current view is the &lt;strong&gt;Admin Console view&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A login credential (&lt;code&gt;yunionauth&lt;/code&gt;) and the &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt;, &lt;code&gt;tenant&lt;/code&gt;, and other cookies are generated in the browser.&lt;/li&gt;
&lt;li&gt;Entering the address again to access the console does not require logging in again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Actual result&lt;/strong&gt;: Pass (verified at the API layer; see Appendix B).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-002-log-in-with-a-wrong-password"&gt;TC-LOGIN-002 Log in with a wrong password&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P0&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: Same as TC-LOGIN-001.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page.&lt;/li&gt;
&lt;li&gt;Enter the username &lt;code&gt;cloudadmin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter the wrong password &lt;code&gt;wrongpass123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Login fails and stays on the login page.&lt;/li&gt;
&lt;li&gt;The page prompts &amp;ldquo;Incorrect username or password&amp;rdquo; (the API returns &lt;code&gt;incorrect username or password&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The password input box is cleared or kept as masked text; no plaintext is echoed.&lt;/li&gt;
&lt;li&gt;No valid login credential is generated and the console cannot be entered.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Actual result&lt;/strong&gt;: Pass. The API returns HTTP 401 with the response body &lt;code&gt;{&amp;quot;class&amp;quot;:&amp;quot;IncorrectUsernameOrPassword&amp;quot;,&amp;quot;code&amp;quot;:401,&amp;quot;details&amp;quot;:&amp;quot;incorrect username or password&amp;quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-003-log-in-with-a-non-existent-username"&gt;TC-LOGIN-003 Log in with a non-existent username&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page.&lt;/li&gt;
&lt;li&gt;Enter the username &lt;code&gt;no_such_user_9999&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter any password.&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Login fails.&lt;/li&gt;
&lt;li&gt;The message is &lt;strong&gt;identical&lt;/strong&gt; to TC-LOGIN-002 and does not distinguish &amp;ldquo;username does not exist&amp;rdquo; from &amp;ldquo;wrong password&amp;rdquo;, so that whether an account exists is not leaked.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-004-submit-with-an-empty-username-or-password"&gt;TC-LOGIN-004 Submit with an empty username or password&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page, leave both the username and password empty, and click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;li&gt;Fill in only the username, leave the password empty, and click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;li&gt;Fill in only the password, leave the username empty, and click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In all three cases no login request is sent and the page stays where it is.&lt;/li&gt;
&lt;li&gt;A validation message &amp;ldquo;Please enter the username&amp;rdquo; / &amp;ldquo;Please enter the password&amp;rdquo; appears below the corresponding input box.&lt;/li&gt;
&lt;li&gt;The input box is highlighted in red or clearly shows an error state.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-005-log-in-with-a-specified-login-domain"&gt;TC-LOGIN-005 Log in with a specified login domain&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: A user that is enabled and allowed to log in to the console exists in the Default domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page and click the &amp;ldquo;Specify login domain&amp;rdquo; entry.&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Default&lt;/code&gt; in the domain list.&lt;/li&gt;
&lt;li&gt;Enter the username &lt;code&gt;cloudadmin&lt;/code&gt; and the password &lt;code&gt;cloudadmin@test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;li&gt;In the user menu in the upper right corner of the console, reopen the login domain setting and click &amp;ldquo;Reset login domain&amp;rdquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After specifying the login domain, the UI shows &amp;ldquo;Current login domain: Default&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Login succeeds and the console opens.&lt;/li&gt;
&lt;li&gt;After resetting the login domain, the login page returns to platform-level login and does not preselect any domain.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-006-login-fails-when-the-domain-and-user-do-not-match"&gt;TC-LOGIN-006 Login fails when the domain and user do not match&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: A domain that cloudadmin has not joined exists (for example a new domain &lt;code&gt;testdomain&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page, click &amp;ldquo;Specify login domain&amp;rdquo;, and select a domain that cloudadmin has not joined.&lt;/li&gt;
&lt;li&gt;Enter the username &lt;code&gt;cloudadmin&lt;/code&gt; and the password &lt;code&gt;cloudadmin@test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;Login&amp;rdquo; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Login fails and prompts that the username or password is incorrect.&lt;/li&gt;
&lt;li&gt;No cross-domain privilege escalation login occurs.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-007-an-image-captcha-appears-after-repeated-wrong-passwords"&gt;TC-LOGIN-007 An image captcha appears after repeated wrong passwords&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: Confirm that a wrong-password threshold is configured on the platform (keystone service parameter &lt;code&gt;password_error_lock_count&lt;/code&gt;; the default 0 means no locking). &lt;strong&gt;This case consumes cloudadmin&amp;rsquo;s error count; confirm the threshold with the administrator before executing it to avoid locking the account.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in repeatedly with cloudadmin and a wrong password until the captcha input box appears in the UI.&lt;/li&gt;
&lt;li&gt;Record the number of failures required to trigger the captcha.&lt;/li&gt;
&lt;li&gt;Enter a wrong captcha in the captcha input box and click &amp;ldquo;Login&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Enter the correct captcha and submit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After the threshold is reached, an image captcha input box appears on the login page and the captcha image can be refreshed.&lt;/li&gt;
&lt;li&gt;When the captcha is wrong, it prompts &amp;ldquo;Please enter a valid captcha&amp;rdquo; / &amp;ldquo;Incorrect captcha&amp;rdquo; and the login is rejected.&lt;/li&gt;
&lt;li&gt;When the captcha is correct and the password is correct, login succeeds.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Remarks&lt;/strong&gt;: If the captcha policy is not enabled on the platform, mark this case as &amp;ldquo;Not applicable&amp;rdquo; and record the platform configuration value.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-008-exceeding-the-wrong-password-limit-locks-the-account-and-generates-a-security-alert"&gt;TC-LOGIN-008 Exceeding the wrong-password limit locks the account and generates a security alert&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: &lt;code&gt;password_error_lock_count&lt;/code&gt; is configured to a value greater than 0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in repeatedly with cloudadmin and a wrong password, exceeding the configured threshold.&lt;/li&gt;
&lt;li&gt;Try to log in with the &lt;strong&gt;correct&lt;/strong&gt; password.&lt;/li&gt;
&lt;li&gt;Log in with an administrator account and view the security alert list.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After the threshold is exceeded, the account is locked; even the correct password cannot log in and a message says the account is locked.&lt;/li&gt;
&lt;li&gt;A &amp;ldquo;Login anomaly&amp;rdquo; alert record is generated on the security alert page.&lt;/li&gt;
&lt;li&gt;The alert is also sent to the locked user, the domain administrator of the user&amp;rsquo;s domain, and the system administrator.&lt;/li&gt;
&lt;li&gt;After the lock is released, the correct password can log in normally.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Remarks&lt;/strong&gt;: &lt;strong&gt;This case locks the cloudadmin account&lt;/strong&gt;. Do not execute it in a production or shared environment; confirm that it can be unlocked before executing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-009-a-user-with-console-login-disabled-cannot-log-in"&gt;TC-LOGIN-009 A user with &amp;ldquo;Console login&amp;rdquo; disabled cannot log in&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: Prepare a local user that is enabled but has &lt;strong&gt;&amp;ldquo;Login to console&amp;rdquo; turned off&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As an administrator, create a user in user management and turn off &amp;ldquo;Login to console&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Log out of the current session and log in to the console with that user.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Login is rejected, prompting that there is no console login permission or that the username or password is incorrect.&lt;/li&gt;
&lt;li&gt;The user can still access via the API and other means (if the platform allows it) but cannot log in to the console.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-010-a-disabled-user-cannot-log-in"&gt;TC-LOGIN-010 A disabled user cannot log in&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: Prepare a user in the disabled state that is currently online.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the console with that user and keep the session online.&lt;/li&gt;
&lt;li&gt;As an administrator, disable the user in user management.&lt;/li&gt;
&lt;li&gt;The user keeps operating on console pages.&lt;/li&gt;
&lt;li&gt;The user logs out and logs in again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The disable operation does &lt;strong&gt;not&lt;/strong&gt; force an online user to log out immediately.&lt;/li&gt;
&lt;li&gt;After the user logs out, logging in again fails and prompts that the user is disabled.&lt;/li&gt;
&lt;li&gt;Logging in with the correct password of the disabled user also fails.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-011-log-in-as-a-user-with-mfa-enabled"&gt;TC-LOGIN-011 Log in as a user with MFA enabled&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: Two-factor authentication is enabled in the global configuration, and the target user has MFA enabled and has completed credential binding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in as a user with MFA enabled and enter the correct username and password.&lt;/li&gt;
&lt;li&gt;Enter the 6-digit security code on the login protection page that appears.&lt;/li&gt;
&lt;li&gt;Submit once with a wrong security code.&lt;/li&gt;
&lt;li&gt;Submit with the correct security code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After the password is verified, the console is not entered directly; instead a 6-digit security code is required.&lt;/li&gt;
&lt;li&gt;A wrong security code prompts &amp;ldquo;Incorrect security code, please re-enter&amp;rdquo; and the console cannot be entered.&lt;/li&gt;
&lt;li&gt;Submitting the correct security code enters the console successfully.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Remarks&lt;/strong&gt;: On test.yunion.io, MFA is currently &lt;strong&gt;not enabled&lt;/strong&gt; for cloudadmin (&lt;code&gt;totp_on=false&lt;/code&gt; in the login credential). Executing this case requires preparing a user with MFA enabled first; if global two-factor authentication is not enabled, the user-side MFA switch does not take effect.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-012-a-user-with-an-expired-password-cannot-log-in"&gt;TC-LOGIN-012 A user with an expired password cannot log in&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: A user with an expired password exists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in as a user whose password has expired.&lt;/li&gt;
&lt;li&gt;Log in as a user whose password expires in 7 days.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A user whose password has expired &lt;strong&gt;cannot&lt;/strong&gt; log in to the system.&lt;/li&gt;
&lt;li&gt;A user whose password expires in 7 days can log in normally, and after login is prompted that the password is about to expire and is advised to change it as soon as possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-013-the-landing-view-after-login-is-correct"&gt;TC-LOGIN-013 The landing view after login is correct&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P0&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: cloudadmin has the admin role of the system project in the Default domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in as cloudadmin.&lt;/li&gt;
&lt;li&gt;Check the view switch entry in the top area.&lt;/li&gt;
&lt;li&gt;Switch to the Domain Admin Console view and the Project view in turn, then switch back to the Admin Console view.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;Admin Console view&lt;/strong&gt; is the default landing view, where system-level resources can be seen.&lt;/li&gt;
&lt;li&gt;The view switch entry is displayed normally, and the left navigation menu changes with the view after switching.&lt;/li&gt;
&lt;li&gt;The Domain Admin Console view requires three-level permissions to be enabled on the platform; when not enabled, that view does not appear.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-014-the-session-becomes-invalid-after-logout"&gt;TC-LOGIN-014 The session becomes invalid after logout&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P0&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the console and record the current address.&lt;/li&gt;
&lt;li&gt;Click the user icon in the upper right corner and select &amp;ldquo;Sign out&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Use the browser back button to return to the page from before the logout.&lt;/li&gt;
&lt;li&gt;Paste the inner page address from before the logout and access it directly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After logout, the page jumps to the login page.&lt;/li&gt;
&lt;li&gt;Going back or directly accessing the inner page is redirected to the login page and no business data is shown.&lt;/li&gt;
&lt;li&gt;The login credential in the browser is invalid.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;After a third-party identity provider (such as CAS) user logs out, if the session on the authentication server side is not destroyed, the user may be logged in again automatically. This is the behavior of the identity provider and needs to be confirmed separately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-015-accessing-an-inner-page-without-logging-in-redirects-to-the-login-page"&gt;TC-LOGIN-015 Accessing an inner page without logging in redirects to the login page&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clear the browser cookies or use an incognito window.&lt;/li&gt;
&lt;li&gt;Access an inner page address directly, such as a resource list address like &lt;code&gt;/vminstance&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The request is intercepted and redirects to the login page.&lt;/li&gt;
&lt;li&gt;After a successful login, the user is redirected back to the inner page address originally accessed (if the platform supports redirecting back).&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-016-switch-the-language-of-the-login-page"&gt;TC-LOGIN-016 Switch the language of the login page&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the login page and switch the UI language to English.&lt;/li&gt;
&lt;li&gt;Switch back to Simplified Chinese.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The login page text switches with the language; the login button, input box placeholders, and error messages are all in the target language.&lt;/li&gt;
&lt;li&gt;Switching the language does not affect the username and password already entered.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-017-log-in-as-an-identity-provider-ldap-user"&gt;TC-LOGIN-017 Log in as an identity provider (LDAP) user&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: An LDAP identity provider is configured and enabled on the platform, and users have been synced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On the login page, select the corresponding identity provider (for example, select the domain corresponding to the identity provider in &amp;ldquo;Specify login domain&amp;rdquo;).&lt;/li&gt;
&lt;li&gt;Enter the username and password of the LDAP user and log in.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identity provider users can log in to the console normally.&lt;/li&gt;
&lt;li&gt;Users marked as from an LDAP identity provider in user management do &lt;strong&gt;not&lt;/strong&gt; support resetting the password on the platform side.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="tc-login-018-login-session-validity-period"&gt;TC-LOGIN-018 Login session validity period&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Priority&lt;/strong&gt;: P2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the console and check the expiration time of the login credential cookie.&lt;/li&gt;
&lt;li&gt;After the page stays idle beyond the credential validity period, operate on the page or access an inner page again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Expected results&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The login credential validity period is 24 hours by default (&lt;code&gt;Max-Age=86399&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;After the credential expires, operating on the page or accessing an inner page redirects to the login page and requires logging in again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Actual result&lt;/strong&gt;: Pass. In the measured login response, the &lt;code&gt;Max-Age&lt;/code&gt; of the four cookies &lt;code&gt;yunionauth&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt;, and &lt;code&gt;tenant&lt;/code&gt; is 86399 seconds.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="appendix-a-api-layer-verification-script"&gt;Appendix A: API-layer verification script&lt;/h2&gt;
&lt;p&gt;Login-related UI behavior can be quickly regression tested with the login API. The following script performs API-level verification against &lt;a href="https://test.yunion.io" target="_blank" rel="noopener noreferrer"&gt;https://test.yunion.io&lt;/a&gt; and can be copied and run directly (&lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;python3&lt;/code&gt; are required).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# verify_login.sh — login API regression verification&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://test.yunion.io&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cloudadmin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cloudadmin@test&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Default&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;== TC-LOGIN-001 Login with correct credentials ==&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s -o /tmp/login_ok.txt -w &lt;span class="s2"&gt;&amp;#34;%{http_code}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c /tmp/login_cookie.txt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -X POST -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s2"&gt;&amp;#34;{\&amp;#34;username\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;,\&amp;#34;password\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PASS&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;,\&amp;#34;domain\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/auth/login&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HTTP &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;200&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PASS: login succeeded&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grep -q yunionauth /tmp/login_cookie.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PASS: yunionauth credential issued&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;FAIL: expected 200, got &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;== TC-LOGIN-002 Login with wrong password ==&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s -w &lt;span class="s2"&gt;&amp;#34;\n%{http_code}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -X POST -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s2"&gt;&amp;#34;{\&amp;#34;username\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;,\&amp;#34;password\&amp;#34;:\&amp;#34;wrongpass123\&amp;#34;,\&amp;#34;domain\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/auth/login&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n -1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tail -n 1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HTTP &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -q IncorrectUsernameOrPassword &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PASS: returned IncorrectUsernameOrPassword&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;FAIL: expected error class not returned&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;== TC-LOGIN-003 Login with non-existent user (message should match wrong password) ==&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s -w &lt;span class="s2"&gt;&amp;#34;\n%{http_code}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -X POST -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s2"&gt;&amp;#34;{\&amp;#34;username\&amp;#34;:\&amp;#34;no_such_user_9999\&amp;#34;,\&amp;#34;password\&amp;#34;:\&amp;#34;whatever\&amp;#34;,\&amp;#34;domain\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/auth/login&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n -1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tail -n 1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HTTP &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -q IncorrectUsernameOrPassword &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PASS: same response as wrong password; account existence not leaked&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;FAIL: error class differs from wrong password&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;== TC-LOGIN-018 Session validity period ==&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s -D - -o /dev/null &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -X POST -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s2"&gt;&amp;#34;{\&amp;#34;username\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;,\&amp;#34;password\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PASS&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;,\&amp;#34;domain\&amp;#34;:\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;}&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/auth/login&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -i &lt;span class="s1"&gt;&amp;#39;^set-cookie: yunionauth&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;Max-Age=[0-9]*&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yunionauth &lt;/span&gt;&lt;span class="nv"&gt;$exp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$exp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Max-Age=86399&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PASS: validity period is 24 hours&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;WARN: validity period differs from expected 86399&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After a successful login, you can use the credential in &lt;code&gt;/tmp/login_cookie.txt&lt;/code&gt; to call other service APIs, for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s -b /tmp/login_cookie.txt &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/servers?limit=5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="appendix-b-measured-records-for-this-environment"&gt;Appendix B: Measured records for this environment&lt;/h2&gt;
&lt;p&gt;The following API behavior was confirmed by measurement on &lt;a href="https://test.yunion.io" target="_blank" rel="noopener noreferrer"&gt;https://test.yunion.io&lt;/a&gt; (2026-09-15) and can serve as the baseline for the expected results of the cases.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check item&lt;/th&gt;
&lt;th&gt;Measured result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Console reachability&lt;/td&gt;
&lt;td&gt;HTTP 200, HTTP/2, nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /api/v1/auth/login&lt;/code&gt;, request body &lt;code&gt;{&amp;quot;username&amp;quot;,&amp;quot;password&amp;quot;,&amp;quot;domain&amp;quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct credentials&lt;/td&gt;
&lt;td&gt;HTTP 200, empty response body, issues &lt;code&gt;yunionauth&lt;/code&gt;/&lt;code&gt;scope&lt;/code&gt;/&lt;code&gt;domain&lt;/code&gt;/&lt;code&gt;tenant&lt;/code&gt; cookies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong password&lt;/td&gt;
&lt;td&gt;HTTP 401, &lt;code&gt;{&amp;quot;class&amp;quot;:&amp;quot;IncorrectUsernameOrPassword&amp;quot;,&amp;quot;details&amp;quot;:&amp;quot;incorrect username or password&amp;quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloudadmin default scope&lt;/td&gt;
&lt;td&gt;&lt;code&gt;system&lt;/code&gt; (Admin Console view)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloudadmin default domain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloudadmin MFA status&lt;/td&gt;
&lt;td&gt;Not enabled (&lt;code&gt;totp_on=false&lt;/code&gt;, &lt;code&gt;totp_verified=false&lt;/code&gt; in the credential)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login credential validity period&lt;/td&gt;
&lt;td&gt;86399 seconds (24 hours)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image captcha API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /api/v1/auth/captcha&lt;/code&gt;, returns &lt;code&gt;image/png&lt;/code&gt; and issues a &lt;code&gt;captcha&lt;/code&gt; cookie&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The above are API-layer measurement conclusions; cases that depend on UI interaction (such as when the captcha appears, MFA interaction, and password expiration prompts) still need to be executed step by step in a browser to confirm.&lt;/p&gt;</description></item></channel></rss>