Simplify password connections

This commit is contained in:
William Brawner 2020-02-05 07:17:02 -06:00
parent 1347b9ea10
commit e1c8ffcd5d
4 changed files with 14 additions and 48 deletions

View file

@ -6,15 +6,11 @@ import androidx.core.content.edit
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.wbrawner.piholeclient.PiHoleApiService import com.wbrawner.piholeclient.PiHoleApiService
import com.wbrawner.piholeclient.Summary
import com.wbrawner.piholeclient.VersionResponse import com.wbrawner.piholeclient.VersionResponse
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.ConnectException import java.net.ConnectException
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.util.regex.Pattern
import java.util.regex.Pattern.DOTALL
const val KEY_BASE_URL = "baseUrl" const val KEY_BASE_URL = "baseUrl"
const val KEY_API_KEY = "apiKey" const val KEY_API_KEY = "apiKey"
@ -56,7 +52,9 @@ class AddPiHelperViewModel(
suspend fun beginScanning(deviceIpAddress: String) { suspend fun beginScanning(deviceIpAddress: String) {
val addressParts = deviceIpAddress.split(".").toMutableList() val addressParts = deviceIpAddress.split(".").toMutableList()
var chunks = 1 var chunks = 1
val ipAddresses = mutableListOf<String>() // If the Pi-hole is correctly set up, then there should be a special host for it as
// "pi.hole"
val ipAddresses = mutableListOf("pi.hole")
while (chunks <= IP_MAX) { while (chunks <= IP_MAX) {
val chunkSize = (IP_MAX - IP_MIN + 1) / chunks val chunkSize = (IP_MAX - IP_MIN + 1) / chunks
if (chunkSize == 1) { if (chunkSize == 1) {
@ -111,16 +109,8 @@ class AddPiHelperViewModel(
} }
suspend fun authenticateWithPassword(password: String) { suspend fun authenticateWithPassword(password: String) {
// Perform the login to get the PHPSESSID cookie set // The Pi-hole API key is just the web password hashed twice with SHA-256
apiService.login(password) authenticateWithApiKey(password.hash().hash())
val html = apiService.getApiToken()
val matcher = Pattern.compile(".*Raw API Token: ([a-z0-9]+).*", DOTALL)
.matcher(html)
if (!matcher.matches()) {
throw RuntimeException("Unable to retrieve API token from password")
}
val apiToken = matcher.group(1)!!
authenticateWithApiKey(apiToken)
} }
suspend fun authenticateWithApiKey(apiKey: String) { suspend fun authenticateWithApiKey(apiKey: String) {

View file

@ -1,3 +1,9 @@
package com.wbrawner.pihelper package com.wbrawner.pihelper
fun <T, R> R.transform(block: (R) -> T): T = block(this) import java.math.BigInteger
import java.security.MessageDigest
fun String.hash(): String = BigInteger(
1,
MessageDigest.getInstance("SHA-256").digest(this.toByteArray())
).toString(16)

View file

@ -15,12 +15,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.MainActivity.Companion.ACTION_FORGET_PIHOLE import com.wbrawner.pihelper.MainActivity.Companion.ACTION_FORGET_PIHOLE
import kotlinx.android.synthetic.main.fragment_main.toolbar
import kotlinx.android.synthetic.main.fragment_info.* import kotlinx.android.synthetic.main.fragment_info.*
import kotlinx.android.synthetic.main.fragment_main.toolbar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.lang.RuntimeException
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class InfoFragment : Fragment(), CoroutineScope { class InfoFragment : Fragment(), CoroutineScope {
@ -30,7 +29,6 @@ class InfoFragment : Fragment(), CoroutineScope {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
throw RuntimeException("I crashed!")
} }
override fun onCreateView( override fun onCreateView(

View file

@ -4,20 +4,14 @@ import com.squareup.moshi.Moshi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import kotlin.reflect.KClass import kotlin.reflect.KClass
interface PiHoleApiService { interface PiHoleApiService {
var baseUrl: String? var baseUrl: String?
var apiKey: String? var apiKey: String?
suspend fun login(password: String): String
suspend fun getApiToken(): String
suspend fun getSummary( suspend fun getSummary(
version: Boolean = false, version: Boolean = false,
type: Boolean = false type: Boolean = false
@ -48,8 +42,6 @@ interface PiHoleApiService {
} }
const val BASE_PATH = "/admin/api.php" const val BASE_PATH = "/admin/api.php"
const val INDEX_PATH = "/admin/index.php"
const val API_TOKEN_PATH = "/admin/scripts/pi-hole/php/api_token.php"
class OkHttpPiHoleApiService( class OkHttpPiHoleApiService(
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
@ -110,26 +102,6 @@ class OkHttpPiHoleApiService(
return sendRequest(request.build(), TopItemsResponse::class)!! return sendRequest(request.build(), TopItemsResponse::class)!!
} }
override suspend fun login(password: String): String {
val url = urlBuilder
.encodedPath(INDEX_PATH)
.addQueryParameter("login", "")
val body = "pw=$password".toRequestBody("application/x-www-form-urlencoded".toMediaType())
val request = Request.Builder()
.post(body)
.url(url.build())
return sendRequest(request.build(), String::class)!!
}
override suspend fun getApiToken(): String {
val url = urlBuilder
.encodedPath(API_TOKEN_PATH)
val request = Request.Builder()
.get()
.url(url.build())
return sendRequest(request.build(), String::class)!!
}
override suspend fun enable(): StatusResponse { override suspend fun enable(): StatusResponse {
val apiToken = this.apiKey ?: throw java.lang.IllegalStateException("No API Token provided") val apiToken = this.apiKey ?: throw java.lang.IllegalStateException("No API Token provided")
val url = urlBuilder val url = urlBuilder