Fix eszkola.opolskie.pl login
This commit is contained in:
parent
5151856da1
commit
f62736adb0
11 changed files with 182 additions and 113 deletions
|
@ -7,6 +7,7 @@ import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
|
|||
import io.github.wulkanowy.sdk.scrapper.login.AccountPermissionException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import org.jsoup.Jsoup
|
||||
|
@ -38,8 +39,12 @@ class ErrorInterceptor : Interceptor {
|
|||
}
|
||||
}
|
||||
|
||||
doc.select(".ErrorMessage, #ErrorTextLabel, #loginArea #errorText").let {
|
||||
if (it.isNotEmpty()) throw BadCredentialsException(it.text().trimEnd('.'))
|
||||
doc.select(".ErrorMessage, #ErrorTextLabel, #loginArea #errorText").takeIf { it.isNotEmpty() }?.let {
|
||||
val errorMessage = it.text().trimEnd('.')
|
||||
if (doc.select(SELECTOR_ADFS).isNotEmpty()) {
|
||||
if (errorMessage.isNotBlank()) throw BadCredentialsException(errorMessage)
|
||||
else logger.warn("Unexpected login page!")
|
||||
} else throw BadCredentialsException(errorMessage)
|
||||
}
|
||||
|
||||
doc.select("#MainPage_ErrorDiv div").let {
|
||||
|
|
|
@ -80,7 +80,11 @@ class LoginHelper(
|
|||
val login = if ("@" in email) email else "EDUPORTAL\\$email"
|
||||
sendADFSMS(login, password)
|
||||
}
|
||||
else -> sendADFS(it, password)
|
||||
"eszkola.opolskie.pl" -> {
|
||||
val login = if ("@" in email) email else "EDUPORTAL\\$email"
|
||||
sendADFSMS(login, password)
|
||||
}
|
||||
else -> sendADFSMS(it, password)
|
||||
}
|
||||
}
|
||||
ADFSLight, ADFSLightScoped, ADFSLightCufs -> sendADFSLightGeneric(it, password, loginType)
|
||||
|
@ -146,8 +150,32 @@ class LoginHelper(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun sendADFS(email: String, password: String): CertificateResponse {
|
||||
val res = api.getForm(getADFSUrl(ADFS))
|
||||
private suspend fun sendADFSMS(email: String, password: String): CertificateResponse {
|
||||
val res = api.sendADFSMSForm(
|
||||
url = getADFSUrl(ADFS),
|
||||
values = mapOf(
|
||||
"UserName" to email,
|
||||
"Password" to password,
|
||||
"AuthMethod" to "FormsAuthentication"
|
||||
)
|
||||
)
|
||||
|
||||
val form = certificateAdapter.fromHtml(res)
|
||||
|
||||
return certificateAdapter.fromHtml(
|
||||
api.sendADFSForm(
|
||||
url = form.action,
|
||||
values = mapOf(
|
||||
"wa" to form.wa,
|
||||
"wresult" to form.wresult,
|
||||
"wctx" to form.wctx
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendADFSCards(email: String, password: String): CertificateResponse {
|
||||
val res = api.getForm(getADFSUrl(ADFSCards))
|
||||
|
||||
if (res.formAction.isBlank()) throw VulcanException("Invalid ADFS login page: '${res.title}'. Try again")
|
||||
val form = certificateAdapter.fromHtml(
|
||||
|
@ -178,73 +206,6 @@ class LoginHelper(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun sendADFSMS(email: String, password: String): CertificateResponse {
|
||||
val res = api.sendADFSMSForm(
|
||||
url = getADFSUrl(ADFS),
|
||||
values = mapOf(
|
||||
"UserName" to email,
|
||||
"Password" to password,
|
||||
"AuthMethod" to "FormsAuthentication"
|
||||
)
|
||||
)
|
||||
|
||||
val form = certificateAdapter.fromHtml(res)
|
||||
|
||||
return certificateAdapter.fromHtml(
|
||||
api.sendADFSForm(
|
||||
url = form.action,
|
||||
values = mapOf(
|
||||
"wa" to form.wa,
|
||||
"wresult" to form.wresult,
|
||||
"wctx" to form.wctx
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendADFSCards(email: String, password: String): CertificateResponse {
|
||||
val form = api.getForm(getADFSUrl(ADFSCards))
|
||||
|
||||
val form2 = api.sendADFSFormStandardChoice(
|
||||
url = "$schema://adfs.$host/${form.formAction.removePrefix("/")}",
|
||||
formState = mapOf(
|
||||
"__db" to form.db,
|
||||
"__VIEWSTATE" to form.viewstate,
|
||||
"__VIEWSTATEGENERATOR" to form.viewStateGenerator,
|
||||
"__EVENTVALIDATION" to form.eventValidation,
|
||||
"PassiveSignInButton.x" to "0",
|
||||
"PassiveSignInButton.y" to "0"
|
||||
)
|
||||
)
|
||||
|
||||
val form3 = certificateAdapter.fromHtml(
|
||||
api.sendADFSForm(
|
||||
url = "$schema://adfs.$host/${form2.formAction.removePrefix("/")}",
|
||||
values = mapOf(
|
||||
"__db" to form2.db,
|
||||
"__VIEWSTATE" to form2.viewstate,
|
||||
"__VIEWSTATEGENERATOR" to form2.viewStateGenerator,
|
||||
"__EVENTVALIDATION" to form2.eventValidation,
|
||||
"SubmitButton.x" to "0",
|
||||
"SubmitButton.y" to "0",
|
||||
"UsernameTextBox" to email,
|
||||
"PasswordTextBox" to password
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return certificateAdapter.fromHtml(
|
||||
api.sendADFSForm(
|
||||
url = form3.action,
|
||||
values = mapOf(
|
||||
"wa" to form3.wa,
|
||||
"wresult" to form3.wresult,
|
||||
"wctx" to form3.wctx
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getADFSUrl(type: Scrapper.LoginType): String {
|
||||
val id = when (type) {
|
||||
ADFS -> if (host == "eduportal.koszalin.pl") "ADFS" else "adfs"
|
||||
|
|
|
@ -21,10 +21,9 @@ class AccountRepository(private val account: AccountService) {
|
|||
|
||||
companion object {
|
||||
const val SELECTOR_STANDARD = ".loginButton, .LogOnBoard input[type=submit]" // remove second selector?
|
||||
const val SELECTOR_ADFS = "form[name=form1] #SubmitButton"
|
||||
const val SELECTOR_ADFS_MS = "form#loginForm"
|
||||
const val SELECTOR_ADFS_LIGHT = ".submit-button, form #SubmitButton"
|
||||
const val SELECTOR_ADFS_CARDS = "#PassiveSignInButton"
|
||||
const val SELECTOR_ADFS = "#loginArea form#loginForm"
|
||||
const val SELECTOR_ADFS_LIGHT = ".submit-button"
|
||||
const val SELECTOR_ADFS_CARDS = "#__VIEWSTATE"
|
||||
}
|
||||
|
||||
suspend fun getPasswordResetCaptcha(registerBaseUrl: String, symbol: String): Pair<String, String> {
|
||||
|
|
|
@ -16,7 +16,6 @@ import io.github.wulkanowy.sdk.scrapper.register.toSemesters
|
|||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_CARDS
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_LIGHT
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_MS
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_STANDARD
|
||||
import io.github.wulkanowy.sdk.scrapper.service.MessagesService
|
||||
import io.github.wulkanowy.sdk.scrapper.service.RegisterService
|
||||
|
@ -63,6 +62,7 @@ class RegisterRepository(
|
|||
|
||||
private suspend fun getSymbols(): List<Pair<String, CertificateResponse>> {
|
||||
val symbolLoginType = getLoginType(startSymbol.getNormalizedSymbol())
|
||||
println("Register login type: $symbolLoginType")
|
||||
val cert = loginHelper.apply { loginType = symbolLoginType }.sendCredentials(email, password)
|
||||
|
||||
return Jsoup.parse(cert.wresult.replace(":", ""), "", Parser.xmlParser())
|
||||
|
@ -83,7 +83,7 @@ class RegisterRepository(
|
|||
val page = register.getFormType(urlGenerator.generate(ServiceManager.UrlGenerator.Site.LOGIN) + "Account/LogOn").page
|
||||
return when {
|
||||
page.select(SELECTOR_STANDARD).isNotEmpty() -> Scrapper.LoginType.STANDARD
|
||||
page.select(SELECTOR_ADFS).isNotEmpty() || page.select(SELECTOR_ADFS_MS).isNotEmpty() -> Scrapper.LoginType.ADFS
|
||||
page.select(SELECTOR_ADFS).isNotEmpty() -> Scrapper.LoginType.ADFS
|
||||
page.select(SELECTOR_ADFS_LIGHT).isNotEmpty() -> {
|
||||
page.selectFirst("form")?.attr("action").orEmpty().run {
|
||||
when {
|
||||
|
|
|
@ -27,10 +27,6 @@ interface LoginService {
|
|||
@GET
|
||||
suspend fun getForm(@Url url: String): ADFSFormResponse
|
||||
|
||||
@POST
|
||||
@FormUrlEncoded
|
||||
suspend fun sendADFSFormStandardChoice(@Url url: String, @FieldMap formState: Map<String, String>): ADFSFormResponse
|
||||
|
||||
@POST
|
||||
@FormUrlEncoded
|
||||
suspend fun sendADFSForm(@Url url: String, @FieldMap values: Map<String, String>): String
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package io.github.wulkanowy.sdk.scrapper
|
||||
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
@Ignore
|
||||
class HostsRemoteTest : BaseTest() {
|
||||
|
||||
private val knownHosts = listOf(
|
||||
"vulcan.net.pl" to "Default",
|
||||
"eszkola.opolskie.pl" to "opole",
|
||||
"edu.gdansk.pl" to "gdansk",
|
||||
"edu.lublin.eu" to "lublin",
|
||||
"umt.tarnow.pl" to "tarnow",
|
||||
"resman.pl" to "rzeszow",
|
||||
"eduportal.koszalin.pl" to "koszalin",
|
||||
"vulcan.net.pl" to "rawamazowiecka",
|
||||
"vulcan.net.pl" to "zdunskawola",
|
||||
"vulcan.net.pl" to "sieradz",
|
||||
"vulcan.net.pl" to "skarzyskokamienna",
|
||||
"vulcan.net.pl" to "lask",
|
||||
"vulcan.net.pl" to "powiatlaski",
|
||||
"vulcan.net.pl" to "powiatkrasnostawski",
|
||||
"vulcan.net.pl" to "powiatketrzynski",
|
||||
"vulcan.net.pl" to "gminaulanmajorat",
|
||||
"vulcan.net.pl" to "gminaozorkow",
|
||||
"vulcan.net.pl" to "gminalopiennikgorny",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun loginTest() = runBlocking {
|
||||
knownHosts.forEach { (host, symbol) ->
|
||||
println("$host/$symbol")
|
||||
val res = runCatching { getScrapper(host, symbol).getStudents() }
|
||||
requireNotNull(res.exceptionOrNull()).cause!!.printStackTrace()
|
||||
assert(res.exceptionOrNull() is BadCredentialsException)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getScrapper(domain: String, startSymbol: String): Scrapper = Scrapper().apply {
|
||||
logLevel = HttpLoggingInterceptor.Level.BASIC
|
||||
loginType = Scrapper.LoginType.AUTO
|
||||
ssl = true
|
||||
host = domain
|
||||
symbol = startSymbol
|
||||
email = "jan@fakelog.cf"
|
||||
password = "jan123"
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ class LoginTest : BaseLocalTest() {
|
|||
|
||||
private val adfs by lazy {
|
||||
LoginHelper(
|
||||
loginType = Scrapper.LoginType.ADFSCards,
|
||||
loginType = Scrapper.LoginType.ADFS,
|
||||
schema = "http",
|
||||
host = "fakelog.localhost:3000",
|
||||
symbol = "default",
|
||||
|
@ -39,7 +39,7 @@ class LoginTest : BaseLocalTest() {
|
|||
okHttp = getOkHttp(
|
||||
errorInterceptor = true,
|
||||
autoLoginInterceptorOn = false,
|
||||
loginType = Scrapper.LoginType.ADFSCards,
|
||||
loginType = Scrapper.LoginType.ADFS,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -48,8 +48,6 @@ class LoginTest : BaseLocalTest() {
|
|||
@Test
|
||||
fun adfsTest() {
|
||||
with(server) {
|
||||
enqueue("ADFS-form-1.html")
|
||||
enqueue("ADFS-form-2.html")
|
||||
enqueue("Logowanie-cufs.html")
|
||||
enqueue("Logowanie-uonet.html")
|
||||
enqueue("Login-success.html")
|
||||
|
@ -118,8 +116,6 @@ class LoginTest : BaseLocalTest() {
|
|||
@Test
|
||||
fun adfsBadCredentialsException() {
|
||||
with(server) {
|
||||
enqueue("ADFS-form-1.html")
|
||||
enqueue("ADFS-form-2.html")
|
||||
enqueue("Logowanie-adfs-zle-haslo.html")
|
||||
start(3000)
|
||||
}
|
||||
|
|
|
@ -117,9 +117,8 @@ class StudentStartRepositoryTest : BaseLocalTest() {
|
|||
|
||||
@Test
|
||||
fun getSemesters_ADFS() {
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS-form-2.html").readText())) //
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS.html").readText())) //
|
||||
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS-form-2.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Logowanie-cufs.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Logowanie-uonet.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Login-success.html").readText()))
|
||||
|
@ -168,10 +167,9 @@ class StudentStartRepositoryTest : BaseLocalTest() {
|
|||
|
||||
@Test
|
||||
fun getSemesters_ADFSCards() {
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS-form-1.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFSCards.html").readText()))
|
||||
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS-form-1.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFS-form-2.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("ADFSCards.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Logowanie-cufs.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Logowanie-uonet.html").readText()))
|
||||
server.enqueue(MockResponse().setBody(LoginTest::class.java.getResource("Login-success.html").readText()))
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Zaloguj</title>
|
||||
</head>
|
||||
<body dir="ltr" class="body">
|
||||
<div id="noScript" style="position:static; width:100%; height:100%; z-index:100">
|
||||
<h1>Wymagana obsługa języka JavaScript</h1>
|
||||
<p>Obsługa języka JavaScript jest wymagana. Ta przeglądarka sieci Web nie obsługuje języka JavaScript lub obsługa języka JavaScript nie została w niej włączona.</p>
|
||||
<p>Aby dowiedzieć się, czy przeglądarka sieci Web obsługuje język JavaScript, lub włączyć obsługę języka JavaScript, zapoznaj się z pomocą przeglądarki sieci Web.</p>
|
||||
</div>
|
||||
<div id="fullPage">
|
||||
<div id="brandingWrapper" class="float">
|
||||
<div id="branding"></div>
|
||||
</div>
|
||||
<div id="contentWrapper" class="float">
|
||||
<div id="content">
|
||||
<div id="header">
|
||||
<img class='logoImage' id='companyLogo' src='/adfs/portal/logo/logo.png?id=6AE4C8BC3FFF3E443B8FE92A692DEC188DE80F9A253AF32B4EE7650537E42A9A' alt='GPE'/>
|
||||
</div>
|
||||
<div id="workArea">
|
||||
<div id="authArea" class="groupMargin">
|
||||
<div id="loginArea">
|
||||
<div id="loginMessage" class="groupMargin">Zaloguj się przy użyciu konta organizacyjnego</div>
|
||||
<form method="post" id="loginForm" autocomplete="off" novalidate="novalidate" onKeyPress="if (event && event.keyCode == 13) Login.submitLoginRequest();" action="/adfs/ls?wa=wsignin1.0&wtrealm=https%3a%2f%2fcufs.edu.gdansk.pl%3a443%2fgdansk%2fAccount%2fLogOn&wctx=rm%3d0%26id%3dadfs%26ru%3d%252fgdansk%252fFS%252fLS%253fwa%253dwsignin1.0%2526wtrealm%253dhttps%25253a%25252f%25252fuonetplus.edu.gdansk.pl%25252fgdansk%25252fLoginEndpoint.aspx%2526wctx%253dhttps%25253a%25252f%25252fuonetplus.edu.gdansk.pl%25252fgdansk%25252fLoginEndpoint.aspx&wct=2021-09-28T15%3a49%3a29Z&client-request-id=47465d7a-d0e3-4c3b-a272-0080010000db" >
|
||||
<div id="error" class="fieldMargin error smallText">
|
||||
<span id="errorText" for=""></span>
|
||||
</div>
|
||||
<div id="formsAuthenticationArea">
|
||||
<div id="userNameArea">
|
||||
<label id="userNameInputLabel" for="userNameInput" class="hidden">Konto użytkownika</label>
|
||||
<input id="userNameInput" name="UserName" type="email" value="" tabindex="1" class="text fullWidth" spellcheck="false" placeholder="osoba@example.com" autocomplete="off"/>
|
||||
</div>
|
||||
<div id="passwordArea">
|
||||
<label id="passwordInputLabel" for="passwordInput" class="hidden">Hasło</label>
|
||||
<input id="passwordInput" name="Password" type="password" tabindex="2" class="text fullWidth" placeholder="Hasło" autocomplete="off"/>
|
||||
</div>
|
||||
<div id="kmsiArea" style="display:none">
|
||||
<input type="checkbox" name="Kmsi" id="kmsiInput" value="true" tabindex="3" />
|
||||
<label for="kmsiInput">Nie wylogowuj mnie</label>
|
||||
</div>
|
||||
<div id="submissionArea" class="submitMargin">
|
||||
<span id="submitButton" class="submit" tabindex="4" role="button" onKeyPress="if (event && event.keyCode == 32) Login.submitLoginRequest();" onclick="return Login.submitLoginRequest();">Zaloguj</span>
|
||||
</div>
|
||||
</div>
|
||||
<input id="optionForms" type="hidden" name="AuthMethod" value="FormsAuthentication"/>
|
||||
</form>
|
||||
<div id="authOptions">
|
||||
<form id="options" method="post" action="https://adfs.edu.gdansk.pl:443/adfs/ls?wa=wsignin1.0&wtrealm=https%3a%2f%2fcufs.edu.gdansk.pl%3a443%2fgdansk%2fAccount%2fLogOn&wctx=rm%3d0%26id%3dadfs%26ru%3d%252fgdansk%252fFS%252fLS%253fwa%253dwsignin1.0%2526wtrealm%253dhttps%25253a%25252f%25252fuonetplus.edu.gdansk.pl%25252fgdansk%25252fLoginEndpoint.aspx%2526wctx%253dhttps%25253a%25252f%25252fuonetplus.edu.gdansk.pl%25252fgdansk%25252fLoginEndpoint.aspx&wct=2021-09-28T15%3a49%3a29Z&client-request-id=47465d7a-d0e3-4c3b-a272-0080010000db">
|
||||
<input id="optionSelection" type="hidden" name="AuthMethod" />
|
||||
<div id='authOptionLinks' class='groupMargin'></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -5,26 +5,25 @@
|
|||
<title>Logowanie do systemu</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/LoginPage.aspx" method="post">
|
||||
<div class="mainDiv" id="mainDiv">
|
||||
<table class="UsernamePasswordTable" id="tableUserCredentials">
|
||||
<tr class="UsernamePasswordTableHeading">
|
||||
<td colspan="2"><label>Proszę podać nazwę użytkownika i hasło</label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="UsernamePasswordTableField"><label for="Username">Nazwa użytkownika:</label></td>
|
||||
<td><input id="Username" name="Username" type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="UsernamePasswordTableField"><label for="Password">Hasło:</label></td>
|
||||
<td><input id="Password" name="Password" type="password"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="SubmitTableField">
|
||||
<input type="image" src="/Resources/g-zaloguj-1.png" id="SubmitButton"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form action="/symbol/LoginPage.aspx" method="post">
|
||||
<div class="label">Zaloguj się</div>
|
||||
<div class="box" id="box">
|
||||
<div class="box-h">Proszę podać nazwę użytkownika i hasło</div>
|
||||
<div class="box-p">
|
||||
<label class="box-line" for="Username">Nazwa użytkownika:</label>
|
||||
<input class="box-line" id="Username" name="Username" type="text"/>
|
||||
</div>
|
||||
<div class="box-p">
|
||||
<label class="box-line" for="Password">Hasło:</label>
|
||||
<input class="box-line" id="Password" name="Password" type="password"/>
|
||||
</div>
|
||||
<div class="box-p box-right">
|
||||
<button type="submit" class="submit-button box-line">Zaloguj się</button>
|
||||
</div>
|
||||
<div>
|
||||
<a class="box-line box-link" id="aUnlock" href="/symbol/AccountManage/UnlockAccountRequest"
|
||||
title="Pierwsze logowanie lub odzyskiwanie hasła">Przywracanie dostępu do konta</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
|
|
Loading…
Reference in a new issue