Skip to content

Commit 64e6b4e

Browse files
authored
Add App Authorization (#46)
1 parent 862b0d2 commit 64e6b4e

File tree

11 files changed

+406
-51
lines changed

11 files changed

+406
-51
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ should take you to the settings area to allow installation if not already enable
7474

7575
### Setup
7676

77+
1. [Install](#Install) and open the app on your device.
78+
2. Go to User Settings and Scan the QR Code. Done.
79+
80+
or
81+
7782
1. [Install](#Install) and open the app on your device.
7883
2. Enter the URL to your Django Files server.
79-
3. Log in as you normally would on the website.
80-
4. All Done! You can now share and open files with Django Files.
84+
3. Log in as you normally would on the website. Done.
8185

8286
To use, share or open any file and choose the Django Files app.
8387
The app will then upload the file to your Django Files server.
@@ -93,7 +97,8 @@ Additionally, the URL is copied to the clipboard and the preview show in the app
9397
- Native File List with Options, Infinite Scroll, and Multi-Select Options.
9498
- Supports Native Local Login, GitHub OAuth, Google OAuth, and Discord OAuth.
9599
- Ability to add multiple servers and switch on the fly from the Server List menu.
96-
- Widget with Stats, Upload Shortcut, Custom Background Update Interval, and More...
100+
- Widget with Stats, Upload Shortcut, Custom Background Update Interval, and More.
101+
- Automatic Authentication from Authenticated Sessions with QR Code or Deep Link.
97102

98103
### Planned
99104

app/src/main/java/com/djangofiles/djangofiles/DailyWorker.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class DailyWorker(appContext: Context, workerParams: WorkerParameters) :
3131

3232
Log.d("DailyWorker", "--- Update Stats")
3333
try {
34-
updateStats(applicationContext)
34+
applicationContext.updateStats()
3535
} catch (e: Exception) {
3636
Log.e("DailyWorker", "ServerApi: Exception: $e")
3737
}
@@ -80,20 +80,20 @@ class DailyWorker(appContext: Context, workerParams: WorkerParameters) :
8080
}
8181
}
8282

83-
suspend fun updateStats(context: Context): Boolean {
83+
suspend fun Context.updateStats(): Boolean {
8484
Log.d("updateStats", "updateStats")
8585
val sharedPreferences =
86-
context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
86+
this.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
8787
val savedUrl = sharedPreferences.getString("saved_url", null).toString()
8888
Log.d("updateStats", "savedUrl: $savedUrl")
89-
val api = ServerApi(context, savedUrl)
89+
val api = ServerApi(this, savedUrl)
9090
val statsResponse = api.current()
9191
Log.d("updateStats", "statsResponse: $statsResponse")
9292
if (statsResponse.isSuccessful) {
9393
val stats = statsResponse.body()
9494
Log.d("updateStats", "stats: $stats")
9595
if (stats != null) {
96-
val dao: ServerDao = ServerDatabase.getInstance(context).serverDao()
96+
val dao: ServerDao = ServerDatabase.getInstance(this).serverDao()
9797
dao.addOrUpdate(
9898
Server(
9999
url = savedUrl,
@@ -107,14 +107,14 @@ suspend fun updateStats(context: Context): Boolean {
107107

108108
// TODO: Make this a reusable function with an event trigger...
109109
Log.i("updateStats", "Updating Widget")
110-
val appWidgetManager = AppWidgetManager.getInstance(context)
111-
val widgetComponent = ComponentName(context, WidgetProvider::class.java)
110+
val appWidgetManager = AppWidgetManager.getInstance(this)
111+
val widgetComponent = ComponentName(this, WidgetProvider::class.java)
112112
val widgetIds = appWidgetManager.getAppWidgetIds(widgetComponent)
113-
val intent = Intent(context, WidgetProvider::class.java).apply {
113+
val intent = Intent(this, WidgetProvider::class.java).apply {
114114
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
115115
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds)
116116
}
117-
context.sendBroadcast(intent)
117+
this.sendBroadcast(intent)
118118

119119
return true
120120
}

app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import android.content.ComponentName
88
import android.content.Context
99
import android.content.Context.CLIPBOARD_SERVICE
1010
import android.content.Intent
11-
import android.content.SharedPreferences
1211
import android.graphics.Color
1312
import android.net.Uri
1413
import android.os.Build
@@ -268,7 +267,7 @@ class MainActivity : AppCompatActivity() {
268267

269268
// Only Handel Intent Once Here after App Start
270269
if (savedInstanceState?.getBoolean("intentHandled") != true) {
271-
handleIntent(intent)
270+
onNewIntent(intent)
272271
}
273272
}
274273

@@ -289,11 +288,6 @@ class MainActivity : AppCompatActivity() {
289288
override fun onNewIntent(intent: Intent) {
290289
super.onNewIntent(intent)
291290
Log.d("onNewIntent", "intent: $intent")
292-
handleIntent(intent)
293-
}
294-
295-
private fun handleIntent(intent: Intent) {
296-
//Log.d("handleIntent", "intent: $intent")
297291
val data = intent.data
298292
val type = intent.type
299293
val action = intent.action
@@ -320,12 +314,19 @@ class MainActivity : AppCompatActivity() {
320314
//val authToken = sharedPreferences.getString("auth_token", null)
321315
//Log.d("handleIntent", "authToken: $authToken")
322316

323-
Log.d("handleIntent", "data?.host: ${data?.host}")
317+
if (savedUrl.isNullOrEmpty()) {
318+
Log.i("handleIntent", "LOCK DRAWER: savedUrl.isNullOrEmpty")
319+
setDrawerLockMode(false)
320+
}
321+
Log.i("handleIntent", "drawerLayout.closeDrawers")
322+
binding.drawerLayout.closeDrawers()
324323

325-
if (data?.host != "oauth" && savedUrl.isNullOrEmpty()) {
324+
Log.d("handleIntent", "data?.host: ${data?.host}")
325+
val noAuthHosts = setOf("oauth", "authorize")
326+
if (data?.host !in noAuthHosts && savedUrl.isNullOrEmpty()) {
326327
Log.i("handleIntent", "Missing Saved URL or Token! Showing Login...")
327328

328-
setDrawerLockMode(false)
329+
//setDrawerLockMode(false)
329330
navController.navigate(
330331
R.id.nav_item_login, null, NavOptions.Builder()
331332
.setPopUpTo(R.id.nav_item_home, true)
@@ -335,7 +336,7 @@ class MainActivity : AppCompatActivity() {
335336
} else if (Intent.ACTION_MAIN == action) {
336337
Log.d("handleIntent", "ACTION_MAIN")
337338

338-
binding.drawerLayout.closeDrawers()
339+
//binding.drawerLayout.closeDrawers()
339340

340341
// TODO: Cleanup the logic for handling MAIN intent...
341342
//val currentDestinationId = navController.currentDestination?.id
@@ -392,7 +393,6 @@ class MainActivity : AppCompatActivity() {
392393
//if (Patterns.WEB_URL.matcher(extraText).matches()) {
393394
if (isURL(extraText)) {
394395
Log.d("handleIntent", "URL DETECTED: $extraText")
395-
binding.drawerLayout.closeDrawers()
396396
val bundle = Bundle().apply {
397397
putString("url", extraText)
398398
}
@@ -448,6 +448,26 @@ class MainActivity : AppCompatActivity() {
448448
processLogout()
449449
} else if ("oauth" == data.host) {
450450
processOauth(data)
451+
} else if ("authorize" == data.host) {
452+
Log.w("handleIntent", "AUTHORIZE QR CODE - DO IT MAN!")
453+
val url = data.getQueryParameter("url")
454+
val signature = data.getQueryParameter("signature")
455+
Log.d("handleIntent", "url: $url")
456+
Log.d("handleIntent", "signature: $signature")
457+
458+
val bundle = Bundle().apply {
459+
putString("url", url)
460+
putString("signature", signature)
461+
}
462+
//navController.navigate(R.id.nav_item_authorize, bundle)
463+
navController.popBackStack(R.id.nav_graph, true)
464+
navController.navigate(
465+
R.id.nav_item_authorize, bundle, NavOptions.Builder()
466+
.setPopUpTo(R.id.nav_item_home, true)
467+
.setLaunchSingleTop(true)
468+
.build()
469+
)
470+
451471
} else {
452472
Toast.makeText(this, "Unknown DeepLink!", Toast.LENGTH_LONG).show()
453473
Log.w("handleIntent", "Unknown DeepLink!")
@@ -477,7 +497,7 @@ class MainActivity : AppCompatActivity() {
477497
private fun showMultiPreview(fileUris: ArrayList<Uri>) {
478498
Log.d("Main[showMultiPreview]", "fileUris: $fileUris")
479499
//fileUris.sort()
480-
binding.drawerLayout.closeDrawers()
500+
//binding.drawerLayout.closeDrawers()
481501
val bundle = Bundle().apply { putParcelableArrayList("fileUris", fileUris) }
482502
navController.popBackStack(R.id.nav_graph, true)
483503
navController.navigate(
@@ -490,7 +510,7 @@ class MainActivity : AppCompatActivity() {
490510

491511
private fun showPreview(uri: Uri?) {
492512
Log.d("Main[showPreview]", "uri: $uri")
493-
binding.drawerLayout.closeDrawers()
513+
//binding.drawerLayout.closeDrawers()
494514
val bundle = Bundle().apply { putString("uri", uri.toString()) }
495515
navController.popBackStack(R.id.nav_graph, true)
496516
navController.navigate(
@@ -578,8 +598,7 @@ class MainActivity : AppCompatActivity() {
578598
val servers = withContext(Dispatchers.IO) { dao.getAll() }
579599
Log.d("processLogout", "servers: $servers")
580600
if (servers.isEmpty()) {
581-
Log.d("processLogout", "NO MORE SERVERS - LOCK TO LOGIN")
582-
//(requireActivity() as MainActivity).setDrawerLockMode(false)
601+
Log.i("handleIntent", "LOCK DRAWER: NO MORE SERVERS")
583602
setDrawerLockMode(false)
584603
navController.navigate(
585604
R.id.nav_item_login, null, NavOptions.Builder()

app/src/main/java/com/djangofiles/djangofiles/ServerApi.kt

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,7 @@ class ServerApi(val context: Context, host: String) {
6767
if (loginResponse.isSuccessful) {
6868
val token = api.getToken().token
6969
Log.d("Api[login]", "token: $token")
70-
val cookies = cookieJar.loadForRequest(hostname.toHttpUrl())
71-
val cookieManager = CookieManager.getInstance()
72-
for (cookie in cookies) {
73-
//Log.d("Api[login]", "setCookie: $cookie")
74-
//cookieManager.setCookie(host, cookie.toString())
75-
if (cookie.name == "sessionid") {
76-
Log.i("Api[login]", "ADDING: ${cookie.name}")
77-
cookieManager.setCookie(hostname, cookie.toString()) {
78-
Log.i("Api[login]", "cookieManager.flush")
79-
cookieManager.flush()
80-
}
81-
}
82-
}
70+
addCookie(token)
8371
Log.i("Api[login]", "SUCCESS")
8472
token
8573
} else {
@@ -92,6 +80,23 @@ class ServerApi(val context: Context, host: String) {
9280
}
9381
}
9482

83+
private fun addCookie(token: String) {
84+
Log.d("Api[addCookie]", "token: $token")
85+
val cookies = cookieJar.loadForRequest(hostname.toHttpUrl())
86+
val cookieManager = CookieManager.getInstance()
87+
for (cookie in cookies) {
88+
//Log.d("Api[login]", "setCookie: $cookie")
89+
//cookieManager.setCookie(host, cookie.toString())
90+
if (cookie.name == "sessionid") {
91+
Log.i("Api[addCookie]", "ADDING: ${cookie.name}")
92+
cookieManager.setCookie(hostname, cookie.toString()) {
93+
Log.i("Api[addCookie]", "cookieManager.flush")
94+
cookieManager.flush()
95+
}
96+
}
97+
}
98+
}
99+
95100
private suspend fun reAuthenticate(): String? {
96101
return try {
97102
val cookies = CookieManager.getInstance().getCookie(hostname)
@@ -115,7 +120,24 @@ class ServerApi(val context: Context, host: String) {
115120
}
116121
}
117122

118-
suspend fun methods(): MethodsResponse {
123+
suspend fun authorize(signature: String): String? {
124+
Log.d("Api[authorize]", "signature: $signature")
125+
// TODO: Make a function from the code in login to handle this response...
126+
val tokenResponse = api.authorize(signature)
127+
Log.d("Api[authorize]", "tokenResponse: $tokenResponse")
128+
if (tokenResponse.isSuccessful) {
129+
val tokenData = tokenResponse.body()
130+
Log.d("Api[authorize]", "tokenData: $tokenData")
131+
if (tokenData != null) {
132+
Log.d("Api[authorize]", "token: ${tokenData.token}")
133+
addCookie(tokenData.token)
134+
return tokenData.token
135+
}
136+
}
137+
return null
138+
}
139+
140+
suspend fun methods(): Response<MethodsResponse> {
119141
Log.d("Api[methods]", "getMethods")
120142
return api.getMethods()
121143
}
@@ -208,8 +230,14 @@ class ServerApi(val context: Context, host: String) {
208230
@Field("password") password: String
209231
): Response<ResponseBody>
210232

233+
@FormUrlEncoded
234+
@POST("auth/application/")
235+
suspend fun authorize(
236+
@Field("signature") signature: String,
237+
): Response<TokenResponse>
238+
211239
@GET("auth/methods/")
212-
suspend fun getMethods(): MethodsResponse
240+
suspend fun getMethods(): Response<MethodsResponse>
213241

214242
@POST("auth/token/")
215243
suspend fun getToken(): TokenResponse

app/src/main/java/com/djangofiles/djangofiles/WidgetProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class WidgetProvider : AppWidgetProvider() {
3939
}
4040
Log.d("Widget[onReceive]", "GlobalScope.launch: START")
4141
GlobalScope.launch(Dispatchers.IO) {
42-
updateStats(context)
42+
context.updateStats()
4343
val appWidgetManager = AppWidgetManager.getInstance(context)
4444
onUpdate(context, appWidgetManager, intArrayOf(appWidgetId))
4545
Log.d("Widget[onReceive]", "GlobalScope.launch: DONE")

app/src/main/java/com/djangofiles/djangofiles/ui/files/FilesFragment.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ private fun Context.deleteConfirmDialog(
614614
}
615615

616616
suspend fun Context.getAlbums(savedUrl: String) {
617+
Log.d("getAlbums", "getAlbums: $savedUrl")
617618
val api = ServerApi(this, savedUrl)
618619
val response = api.albums()
619620
Log.d("getAlbums", "response: $response")

0 commit comments

Comments
 (0)