Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ ownCloud admins and users.
* Enhancement - Polish UI and sync operations over Kiteworks servers: [#4591](https://github.com/owncloud/android/issues/4591)
* Enhancement - Integration of instrumented tests in GitHub Actions: [#4595](https://github.com/owncloud/android/issues/4595)
* Enhancement - SBOM (Software Bill of Materials): [#4598](https://github.com/owncloud/android/issues/4598)
* Enhancement - New set of configurations for Kiteworks servers: [#4622](https://github.com/owncloud/android/issues/4622)

## Details

Expand Down Expand Up @@ -223,6 +224,14 @@ ownCloud admins and users.
https://github.com/owncloud/android/pull/4599
https://github.com/owncloud/android/pull/4621

* Enhancement - New set of configurations for Kiteworks servers: [#4622](https://github.com/owncloud/android/issues/4622)

A new set of configurations in setup file has been added to support the
connection to Kiteworks servers.

https://github.com/owncloud/android/issues/4622
https://github.com/owncloud/android/pull/4632

# Changelog for ownCloud Android Client [4.5.1] (2025-04-03)

The following sections list the changes in ownCloud Android Client 4.5.1 relevant to
Expand Down
6 changes: 6 additions & 0 deletions changelog/unreleased/4632
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: New set of configurations for Kiteworks servers

A new set of configurations in setup file has been added to support the connection to Kiteworks servers.

https://github.com/owncloud/android/issues/4622
https://github.com/owncloud/android/pull/4632
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* @author Christian Schabesberger
* @author David González Verdugo
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -346,13 +347,23 @@ private String refreshToken(
String clientIdForRequest = null;
String clientSecretForRequest = null;

boolean isKiteworksServer = Boolean.parseBoolean(accountManager.getUserData(account, AccountUtils.Constants.KEY_IS_KITEWORKS_SERVER));

if (clientId == null) {
Timber.d("Client Id not stored. Let's use the hardcoded one");
clientId = mContext.getString(R.string.oauth2_client_id);
if (isKiteworksServer) {
clientId = mContext.getString(R.string.kiteworks_client_id);
} else {
clientId = mContext.getString(R.string.oauth2_client_id);
}
}
if (clientSecret == null) {
Timber.d("Client Secret not stored. Let's use the hardcoded one");
clientSecret = mContext.getString(R.string.oauth2_client_secret);
if (isKiteworksServer) {
clientSecret = mContext.getString(R.string.kiteworks_client_secret);
} else {
clientSecret = mContext.getString(R.string.oauth2_client_secret);
}
}

if (oidcServerConfigurationUseCaseResult.isSuccess()) {
Expand All @@ -376,7 +387,12 @@ private String refreshToken(

String clientAuth = OAuthUtils.Companion.getClientAuth(clientSecret, clientId);

String scope = mContext.getResources().getString(R.string.oauth2_openid_scope);
String scope;
if (isKiteworksServer) {
scope = mContext.getResources().getString(R.string.kiteworks_openid_scope);
} else {
scope = mContext.getResources().getString(R.string.oauth2_openid_scope);
}

TokenRequest oauthTokenRequest = new TokenRequest.RefreshToken(
baseUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.owncloud.android.data.authentication.KEY_USER_ID
import com.owncloud.android.databinding.AccountSetupBinding
import com.owncloud.android.domain.authentication.oauth.model.ResponseType
import com.owncloud.android.domain.authentication.oauth.model.TokenRequest
import com.owncloud.android.domain.authentication.oauth.model.TokenResponse
import com.owncloud.android.domain.exceptions.NoNetworkConnectionException
import com.owncloud.android.domain.exceptions.OwncloudVersionNotSupportedException
import com.owncloud.android.domain.exceptions.SSLErrorCode
Expand Down Expand Up @@ -107,6 +108,7 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
private lateinit var serverBaseUrl: String

private var oidcSupported = false
private var isKiteworksServer = false

private lateinit var binding: AccountSetupBinding

Expand Down Expand Up @@ -548,13 +550,16 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
val customTabsBuilder: CustomTabsIntent.Builder = CustomTabsIntent.Builder()
val customTabsIntent: CustomTabsIntent = customTabsBuilder.build()

val serverInfo = authenticationViewModel.serverInfo.value?.peekContent()?.getStoredData()
isKiteworksServer = serverInfo is ServerInfo.OIDCServer && serverInfo.oidcServerConfiguration.isKiteworksServer

val authorizationEndpointUri = OAuthUtils.buildAuthorizationRequest(
authorizationEndpoint = authorizationEndpoint,
redirectUri = OAuthUtils.buildRedirectUri(applicationContext).toString(),
clientId = clientId,
redirectUri = OAuthUtils.buildRedirectUri(applicationContext, isKiteworksServer).toString(),
clientId = if (isKiteworksServer) getString(R.string.kiteworks_client_id) else clientId,
responseType = ResponseType.CODE.string,
scope = if (oidcSupported) mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_SCOPE, R.string.oauth2_openid_scope) else "",
prompt = if (oidcSupported) mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_PROMPT, R.string.oauth2_openid_prompt) else "",
scope = getScope(),
prompt = getPrompt(),
codeChallenge = authenticationViewModel.codeChallenge,
state = authenticationViewModel.oidcState,
username = username,
Expand All @@ -574,6 +579,33 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
}
}

private fun getScope(): String =
if (isKiteworksServer) {
getString(R.string.kiteworks_openid_scope)
} else if (oidcSupported) {
mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_SCOPE, R.string.oauth2_openid_scope)
} else {
""
}
Comment thread
jesmrec marked this conversation as resolved.

private fun getScopeForLogin(tokenResponse: TokenResponse): String? =
if (isKiteworksServer) {
getString(R.string.kiteworks_openid_scope)
} else if (oidcSupported) {
mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_SCOPE, R.string.oauth2_openid_scope)
} else {
tokenResponse.scope
}
Comment thread
jesmrec marked this conversation as resolved.

private fun getPrompt(): String =
if (isKiteworksServer) {
getString(R.string.kiteworks_openid_prompt)
} else if (oidcSupported) {
mdmProvider.getBrandingString(CONFIGURATION_OAUTH2_OPEN_ID_PROMPT, R.string.oauth2_openid_prompt)
} else {
""
}
Comment thread
jesmrec marked this conversation as resolved.

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.let {
Expand Down Expand Up @@ -618,7 +650,11 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
OAuthUtils.getClientAuth(clientRegistrationInfo.clientSecret as String, clientRegistrationInfo.clientId)

} else {
OAuthUtils.getClientAuth(getString(R.string.oauth2_client_secret), getString(R.string.oauth2_client_id))
if (isKiteworksServer) {
OAuthUtils.getClientAuth(getString(R.string.kiteworks_client_secret), getString(R.string.kiteworks_client_id))
} else {
OAuthUtils.getClientAuth(getString(R.string.oauth2_client_secret), getString(R.string.oauth2_client_id))
}
Comment thread
jesmrec marked this conversation as resolved.
}

// Use oidc discovery one, or build an oauth endpoint using serverBaseUrl + Setup string.
Expand All @@ -631,24 +667,25 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
if (serverInfo is ServerInfo.OIDCServer) {
tokenEndPoint = serverInfo.oidcServerConfiguration.tokenEndpoint
if (serverInfo.oidcServerConfiguration.isTokenEndpointAuthMethodSupportedClientSecretPost()) {
clientId = clientRegistrationInfo?.clientId ?: contextProvider.getString(R.string.oauth2_client_id)
clientSecret = clientRegistrationInfo?.clientSecret ?: contextProvider.getString(R.string.oauth2_client_secret)
val defaultClientId = if (isKiteworksServer) R.string.kiteworks_client_id else R.string.oauth2_client_id
val defaultClientSecret = if (isKiteworksServer) R.string.kiteworks_client_secret else R.string.oauth2_client_secret

clientId = clientRegistrationInfo?.clientId ?: contextProvider.getString(defaultClientId)
clientSecret = clientRegistrationInfo?.clientSecret ?: contextProvider.getString(defaultClientSecret)
}
} else {
tokenEndPoint = "$serverBaseUrl${File.separator}${contextProvider.getString(R.string.oauth2_url_endpoint_access)}"
}

val scope = resources.getString(R.string.oauth2_openid_scope)

val requestToken = TokenRequest.AccessToken(
baseUrl = serverBaseUrl,
tokenEndpoint = tokenEndPoint,
clientAuth = clientAuth,
scope = scope,
scope = getScope(),
clientId = clientId,
clientSecret = clientSecret,
authorizationCode = authorizationCode,
redirectUri = OAuthUtils.buildRedirectUri(applicationContext).toString(),
redirectUri = OAuthUtils.buildRedirectUri(applicationContext, isKiteworksServer).toString(),
codeVerifier = authenticationViewModel.codeVerifier
)

Expand All @@ -667,10 +704,7 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted
authTokenType = OAUTH_TOKEN_TYPE,
accessToken = tokenResponse.accessToken,
refreshToken = tokenResponse.refreshToken.orEmpty(),
scope = if (oidcSupported) mdmProvider.getBrandingString(
CONFIGURATION_OAUTH2_OPEN_ID_SCOPE,
R.string.oauth2_openid_scope,
) else tokenResponse.scope,
scope = getScopeForLogin(tokenResponse),
updateAccountWithUsername = if (loginAction != ACTION_CREATE) userAccount?.name else null,
clientRegistrationInfo = clientRegistrationInfo
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author David González Verdugo
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2024 ownCloud GmbH
* Copyright (C) 2025 ownCloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -123,11 +124,26 @@ class OAuthUtils {
}
}.build()

fun buildRedirectUri(context: Context): Uri =
Uri.Builder()
.scheme(context.getString(R.string.oauth2_redirect_uri_scheme))
.authority(context.getString(R.string.oauth2_redirect_uri_host))
.path(context.getString(R.string.oauth2_redirect_uri_path))
fun buildRedirectUri(context: Context, isKiteworksServer: Boolean = false): Uri {
val scheme: String
val host: String
val path: String

if (isKiteworksServer) {
scheme = context.getString(R.string.kiteworks_redirect_uri_scheme)
host = context.getString(R.string.kiteworks_redirect_uri_host)
path = context.getString(R.string.kiteworks_redirect_uri_path)
} else {
scheme = context.getString(R.string.oauth2_redirect_uri_scheme)
host = context.getString(R.string.oauth2_redirect_uri_host)
path = context.getString(R.string.oauth2_redirect_uri_path)
}

return Uri.Builder()
.scheme(scheme)
.authority(host)
.path(path)
.build()
}
}
}
9 changes: 9 additions & 0 deletions owncloudApp/src/main/res/values/setup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@
<string name="oauth2_client_id">e4rAsNUSIUs0lF4nbv9FmCeUkTlV9GdgTLDH1b5uie7syb90SzEVrbN7HIpmWJeD</string>
<string name="oauth2_client_secret">dInFYGV33xKzhbRmpqQltYNdfLdJIfJ9L5ISoKhNoT9qZftpdWSP71VrpGR9pmoD</string>

<!-- Configuration for Kiteworks severs -->
<string name="kiteworks_client_id">163d022d-f2bf-55a7-9cdb-45507fa21cd5</string>
Comment thread
jesmrec marked this conversation as resolved.
<string name="kiteworks_client_secret"></string>
<string name="kiteworks_redirect_uri_scheme">oc</string>
<string name="kiteworks_redirect_uri_host">android.owncloud.com</string>
<string name="kiteworks_redirect_uri_path">/</string>
Comment thread
jesmrec marked this conversation as resolved.
<string name="kiteworks_openid_scope"></string>
<string name="kiteworks_openid_prompt"></string>

<!-- To enable/disable app rate feature -->
<bool name="enable_rate_me_feature">true</bool>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class ConnectionValidator(

client.account = baseClient.account
client.credentials = baseClient.credentials
client.isKiteworksServer = baseClient.isKiteworksServer

while (validationRetryCount < VALIDATION_RETRY_COUNT) {
Timber.d("validationRetryCount %d", validationRetryCount)
var successCounter = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class OwnCloudAccount {

private Account mSavedAccount;

private Boolean mIsKiteworksServer;

/**
* Constructor for already saved OC accounts.
* <p>
Expand All @@ -73,13 +75,14 @@ public OwnCloudAccount(Account savedAccount, Context context) throws AccountNotF
mSavedAccountName = savedAccount.name;
mCredentials = null; // load of credentials is delayed

AccountManager ama = AccountManager.get(context.getApplicationContext());
String baseUrl = ama.getUserData(mSavedAccount, AccountUtils.Constants.KEY_OC_BASE_URL);
AccountManager accountManager = AccountManager.get(context.getApplicationContext());
String baseUrl = accountManager.getUserData(mSavedAccount, AccountUtils.Constants.KEY_OC_BASE_URL);
if (baseUrl == null) {
throw new AccountNotFoundException(mSavedAccount, "Account not found", null);
}
mBaseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, mSavedAccount));
mDisplayName = ama.getUserData(mSavedAccount, AccountUtils.Constants.KEY_DISPLAY_NAME);
mDisplayName = accountManager.getUserData(mSavedAccount, AccountUtils.Constants.KEY_DISPLAY_NAME);
mIsKiteworksServer = Boolean.parseBoolean(accountManager.getUserData(mSavedAccount, AccountUtils.Constants.KEY_IS_KITEWORKS_SERVER));
}

/**
Expand Down Expand Up @@ -149,4 +152,8 @@ public String getDisplayName() {
return null;
}
}
}

public Boolean getIsKiteworksServer() {
return mIsKiteworksServer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception {

HttpUrl originalUrl = method.getHttpUrl();
String encodedPath = originalUrl.encodedPath();
if (mIsKiteworksServer && !encodedPath.startsWith(WELL_KNOWN_PATH) && !encodedPath.equals(OAUTH_TOKEN_PATH)) {
if (mIsKiteworksServer && !encodedPath.startsWith(WELL_KNOWN_PATH) && !encodedPath.equals(OAUTH_TOKEN_PATH)
&& !encodedPath.contains(KWDAV_PATH)) {
HttpUrl newUrl = originalUrl.newBuilder().encodedPath(KWDAV_PATH + encodedPath).build();
method.setUrl(newUrl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ public OwnCloudClient getClientFor(OwnCloudAccount account,
account.loadCredentials(context);
client.setCredentials(account.getCredentials());

client.setIsKiteworksServer(account.getIsKiteworksServer());

if (accountName != null) {
mClientsWithKnownUsername.put(accountName, client);
Timber.v("new client for account %s", accountName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ public static class Constants {
*/
public static final String KEY_DISPLAY_NAME = "oc_display_name";

public static final String KEY_IS_KITEWORKS_SERVER = "is_kiteworks_server";

public static final int ACCOUNT_VERSION = 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author David González Verdugo
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -51,6 +52,8 @@ const val KEY_CLIENT_REGISTRATION_CLIENT_ID = "client_id"
const val KEY_CLIENT_REGISTRATION_CLIENT_SECRET = "client_secret"
const val KEY_CLIENT_REGISTRATION_CLIENT_EXPIRATION_DATE = "client_secret_expires_at"

const val KEY_IS_KITEWORKS_SERVER = "is_kiteworks_server"

/** Query parameters to retrieve the authorization code. More info: https://tools.ietf.org/html/rfc6749#section-4.1.1 */
const val QUERY_PARAMETER_REDIRECT_URI = "redirect_uri"
const val QUERY_PARAMETER_CLIENT_ID = "client_id"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* ownCloud Android client application
*
* @author David González Verdugo
* Copyright (C) 2020 ownCloud GmbH.
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand All @@ -28,6 +30,7 @@ import com.owncloud.android.data.authentication.KEY_CLIENT_REGISTRATION_CLIENT_I
import com.owncloud.android.data.authentication.KEY_CLIENT_REGISTRATION_CLIENT_SECRET
import com.owncloud.android.data.authentication.KEY_FEATURE_ALLOWED
import com.owncloud.android.data.authentication.KEY_FEATURE_SPACES
import com.owncloud.android.data.authentication.KEY_IS_KITEWORKS_SERVER
import com.owncloud.android.data.authentication.KEY_OAUTH2_REFRESH_TOKEN
import com.owncloud.android.data.authentication.KEY_OAUTH2_SCOPE
import com.owncloud.android.data.authentication.SELECTED_ACCOUNT
Expand Down Expand Up @@ -168,6 +171,12 @@ class OCLocalAuthenticationDataSource(
// Only fresh accounts will support spaces
accountManager.setUserData(newAccount, KEY_FEATURE_SPACES, KEY_FEATURE_ALLOWED)

if (serverInfo is ServerInfo.OIDCServer && serverInfo.oidcServerConfiguration.isKiteworksServer) {
accountManager.setUserData(newAccount, KEY_IS_KITEWORKS_SERVER, "true")
} else {
accountManager.setUserData(newAccount, KEY_IS_KITEWORKS_SERVER, "false")
}

/// add the new account as default in preferences, if there is none already
val defaultAccount: Account? = getCurrentAccount()
if (defaultAccount == null) {
Expand Down
Loading
Loading