/**
* Methods for accessing and managing access groups
*
* @module ElvClient/AccessGroups
*/
/*
const LibraryContract = require("../contracts/BaseLibrary");
const AccessGroupContract = require("../contracts/BaseAccessControlGroup");
const AccessIndexorContract = require("../contracts/AccessIndexor");
*/
const {
ValidatePresence,
ValidateLibrary,
ValidateObject,
ValidateAddress,
ValidateUserWallet
} = require("../Validation");
/**
* Retrieve a list of all available access groups
*
* @methodGroup Access Groups
* @return {Promise<Array>} - List of access groups
*/
exports.ListAccessGroups = async function() {
const addresses = (await this.Collection({collectionType: "accessGroups"}))
.map(address => this.utils.FormatAddress(address));
const groups = await this.utils.LimitedMap(
5,
addresses,
async address => {
try {
const id = this.utils.AddressToHash(address);
const meta = (await this.ContentObjectMetadata({
libraryId: this.contentSpaceLibraryId,
objectId: `iq__${id}`
})) || {};
return {
address,
id: `igrp${id}`,
meta
};
} catch(error) {
this.Log(error, true);
}
}
);
return groups
.filter(g => g)
.sort((a, b) => {
const name1 = (a.meta.public || {}).name || `zz__${a.address}`;
const name2 = (b.meta.public || {}).name || `zz__${b.address}`;
return name1 < name2 ? -1 : 1;
});
};
exports.SetGroupPermission = async function({groupAddress, objectId, permission, remove=false}) {
const groupInfo = await this.authClient.ContractInfo({address: groupAddress});
const objectInfo = await this.authClient.ContractInfo({id: objectId});
// check if ContractInfo returned an empty object for groupAddress
if(Object.keys(groupInfo).length === 0) throw Error(`No information returned for group address ${groupAddress}`);
let event;
if(!objectInfo.isV3 && objectInfo.accessType === this.authClient.ACCESS_TYPES.GROUP) {
// V2 access group does not have "setRights" method
if(groupInfo.isV3) {
throw Error("Unable to map V3 group to V2 object");
}
event = await this.CallContractMethodAndWait({
contractAddress: groupAddress,
methodName: "setAccessGroupRights",
methodArgs: [
this.utils.HashToAddress(objectId),
permission === "manage" ? 2 : (permission === "access" ? 1 : 0),
permission === "none" || remove ? 0 : 2
]
});
} else {
event = await this.CallContractMethodAndWait({
contractAddress: this.utils.HashToAddress(objectId),
methodName: "setRights",
methodArgs: [
groupAddress,
permission === "manage" ? 2 : (permission === "access" ? 1 : 0),
permission === "none" || remove ? 0 : 2
]
});
}
return await this.ExtractEventFromLogs({
abi: groupInfo.abi,
event,
eventName: "RightsChanged"
});
};
/**
* Returns the address of the owner of the specified content object
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} libraryId
*
* @returns {Promise<string>} - The account address of the owner
*/
exports.AccessGroupOwner = async function({contractAddress}) {
contractAddress = ValidateAddress(contractAddress);
this.Log(`Retrieving owner of access group ${contractAddress}`);
return await this.authClient.Owner({address: contractAddress});
};
/**
* Get a list of addresses of members of the specified group
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param contractAddress - The address of the access group contract
*
* @return {Promise<Array<string>>} - List of member addresses
*/
exports.AccessGroupMembers = async function({contractAddress}) {
contractAddress = ValidateAddress(contractAddress);
this.Log(`Retrieving members for group ${contractAddress}`);
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "membersList"
});
if(hasMethod) {
const length = (await this.CallContractMethod({
contractAddress,
methodName: "membersNum"
})).toNumber();
return await Promise.all(
[...Array(length)].map(async (_, i) =>
this.utils.FormatAddress(
await this.CallContractMethod({
contractAddress,
methodName: "membersList",
methodArgs: [i]
})
)
)
);
} else {
const response = this.utils.FromHex(
await this.CallContractMethod({
contractAddress,
methodName: "getMeta",
methodArgs: ["members"]
})
);
return response && JSON.parse(response) ? JSON.parse(response) : [];
}
};
/**
* Get a list of addresses of managers of the specified group
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param contractAddress - The address of the access group contract
*
* @return {Promise<Array<string>>} - List of manager addresses
*/
exports.AccessGroupManagers = async function({contractAddress}) {
contractAddress = ValidateAddress(contractAddress);
this.Log(`Retrieving managers for group ${contractAddress}`);
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "managersList"
});
if(hasMethod) {
const length = (await this.CallContractMethod({
contractAddress,
methodName: "managersNum"
})).toNumber();
return await Promise.all(
[...Array(length)].map(async (_, i) =>
this.utils.FormatAddress(
await this.CallContractMethod({
contractAddress,
methodName: "managersList",
methodArgs: [i]
})
)
)
);
} else {
const response = this.utils.FromHex(
await this.CallContractMethod({
contractAddress,
methodName: "getMeta",
methodArgs: ["managers"]
})
);
return response && JSON.parse(response) ? JSON.parse(response) : [];
}
};
/**
* Create a access group
*
* A new access group contract is deployed from the content space
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string=} name - Name of the access group
* @param {string=} description - Description for the access group
* @param {object=} meta - Metadata for the access group
*
* @returns {Promise<string>} - Contract address of created access group
*/
exports.CreateAccessGroup = async function({name, description, metadata={}, visibility=1}={}) {
this.Log(`Creating access group: ${name || ""} ${description || ""}`);
let { contractAddress } = await this.authClient.CreateAccessGroup();
contractAddress = this.utils.FormatAddress(contractAddress);
const objectId = this.utils.AddressToObjectId(contractAddress);
const tenantContractId = await this.userProfileClient.TenantContractId();
this.Log(`Access group: ${contractAddress} ${objectId}`);
const editResponse = await this.EditContentObject({
libraryId: this.contentSpaceLibraryId,
objectId: objectId
});
const groupMetadata = {
public: {
name,
description
},
name,
description,
...metadata
};
await this.ReplaceMetadata({
libraryId: this.contentSpaceLibraryId,
objectId,
writeToken: editResponse.write_token,
metadata: groupMetadata
});
await this.CallContractMethodAndWait({
contractAddress,
methodName: "setVisibility",
methodArgs: [visibility],
});
await this.FinalizeContentObject({
libraryId: this.contentSpaceLibraryId,
objectId,
writeToken: editResponse.write_token,
commitMessage: "Create access group"
});
if(tenantContractId){
const tenantInfo = await this.SetTenantContractId({contractAddress, tenantContractId});
if(tenantInfo.tenantId) {
let tenantAdminGroupAddress = this.utils.HashToAddress(tenantInfo.tenantId);
await this.AddContentObjectGroupPermission({
objectId,
groupAddress: tenantAdminGroupAddress,
permission: "manage"
});
} else {
// eslint-disable-next-line no-console
console.warn("No tenant ID associated with current tenant.");
}
}
return contractAddress;
};
/**
* NOT YET SUPPORTED - Delete an access group
*
* Calls the kill method on the specified access group's contract
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} contractAddress - The address of the access group contract
*/
exports.DeleteAccessGroup = async function({contractAddress}) {
throw Error("Not supported");
// eslint-disable-next-line no-unreachable
contractAddress = ValidateAddress(contractAddress);
this.Log(`Deleting access group ${contractAddress}`);
await this.CallContractMethodAndWait({
contractAddress,
methodName: "kill",
methodArgs: []
});
};
exports.AccessGroupMembershipMethod = async function({
contractAddress,
memberAddress,
methodName,
eventName
}) {
contractAddress = ValidateAddress(contractAddress);
memberAddress = ValidateAddress(memberAddress);
// Ensure caller is the member being acted upon or a manager/owner of the group
if(!this.utils.EqualAddress(this.signer.address, memberAddress)) {
const isManager = await this.CallContractMethod({
contractAddress,
methodName: "hasManagerAccess",
methodArgs: [this.utils.FormatAddress(this.signer.address)]
});
if(!isManager) {
throw Error("Manager access required");
}
}
this.Log(`Calling ${methodName} on group ${contractAddress} for user ${memberAddress}`);
const event = await this.CallContractMethodAndWait({
contractAddress,
methodName,
methodArgs: [memberAddress],
eventName,
eventValue: "candidate",
});
const abi = await this.ContractAbi({contractAddress});
const candidate = this.ExtractValueFromEvent({
abi,
event,
eventName,
eventValue: "candidate"
});
if(this.utils.FormatAddress(candidate) !== this.utils.FormatAddress(memberAddress)) {
// eslint-disable-next-line no-console
console.error("Mismatch: " + candidate + " :: " + memberAddress);
// eslint-disable-next-line no-console
console.error("Is target user address wallet created?");
throw Error("Access group method " + methodName + " failed. Is target user address wallet created?");
}
return event.transactionHash;
};
/**
* Add a member to the access group at the specified contract address. This client's signer must
* be a manager of the access group.
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} contractAddress - Address of the access group contract
* @param {string} memberAddress - Address of the member to add
*
* @returns {Promise<string>} - The transaction hash of the call to the grantAccess method
*/
exports.AddAccessGroupMember = async function({contractAddress, memberAddress}) {
contractAddress = ValidateAddress(contractAddress);
memberAddress = ValidateAddress(memberAddress);
await ValidateUserWallet({address: memberAddress, client: this});
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "membersList"
});
const response = await this.AccessGroupMembershipMethod({
contractAddress,
memberAddress,
methodName: "grantAccess",
eventName: "MemberAdded"
});
if(!hasMethod) {
let memberList = await this.AccessGroupMembers({
contractAddress
});
memberList.push(memberAddress);
memberList = memberList.filter((value, index, self) => {
return self.indexOf(value) === index;
});
await this.ReplaceContractMetadata({
contractAddress,
metadataKey: "members",
metadata: memberList
});
}
return response;
};
/**
* Remove a member from the access group at the specified contract address. This client's signer must
* be a manager of the access group.
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} contractAddress - Address of the access group contract
* @param {string} memberAddress - Address of the member to remove
*
* @returns {Promise<string>} - The transaction hash of the call to the revokeAccess method
*/
exports.RemoveAccessGroupMember = async function({contractAddress, memberAddress}) {
contractAddress = ValidateAddress(contractAddress);
memberAddress = ValidateAddress(memberAddress);
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "membersList"
});
const response = await this.AccessGroupMembershipMethod({
contractAddress,
memberAddress,
methodName: "revokeAccess",
eventName: "MemberRevoked"
});
if(!hasMethod) {
let memberList = await this.AccessGroupMembers({
contractAddress
});
memberList = memberList.filter(element => element !== memberAddress);
return await this.ReplaceContractMetadata({
contractAddress,
metadataKey: "members",
metadata: memberList
});
}
return response;
};
/**
* Add a manager to the access group at the specified contract address. This client's signer must
* be a manager of the access group.
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} contractAddress - Address of the access group contract
* @param {string} memberAddress - Address of the manager to add
*
* @returns {Promise<string>} - The transaction hash of the call to the grantManagerAccess method
*/
exports.AddAccessGroupManager = async function({contractAddress, memberAddress}) {
contractAddress = ValidateAddress(contractAddress);
memberAddress = ValidateAddress(memberAddress);
await ValidateUserWallet({address: memberAddress, client: this});
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "membersList"
});
const response = await this.AccessGroupMembershipMethod({
contractAddress,
memberAddress,
methodName: "grantManagerAccess",
eventName: "ManagerAccessGranted"
});
if(!hasMethod) {
let managerList = await this.AccessGroupManagers({
contractAddress
});
managerList.push(memberAddress);
managerList = managerList.filter((value, index, self) => {
return self.indexOf(value) === index;
});
await this.ReplaceContractMetadata({
contractAddress,
metadataKey: "managers",
metadata: managerList
});
}
return response;
};
/**
* Remove a manager from the access group at the specified contract address. This client's signer must
* be a manager of the access group.
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Access Groups
* @namedParams
* @param {string} contractAddress - Address of the access group contract
* @param {string} memberAddress - Address of the manager to remove
*
* @returns {Promise<string>} - The transaction hash of the call to the revokeManagerAccess method
*/
exports.RemoveAccessGroupManager = async function({contractAddress, memberAddress}) {
contractAddress = ValidateAddress(contractAddress);
memberAddress = ValidateAddress(memberAddress);
const hasMethod = await this.authClient.ContractHasMethod({
contractAddress,
methodName: "managersList"
});
if(!hasMethod) {
let managerList = await this.AccessGroupManagers({
contractAddress
});
managerList = managerList.filter(element => element !== memberAddress);
await this.ReplaceContractMetadata({
contractAddress,
metadataKey: "managers",
metadata: managerList
});
}
const response = await this.AccessGroupMembershipMethod({
contractAddress,
memberAddress,
methodName: "revokeManagerAccess",
eventName: "ManagerAccessRevoked"
});
return response;
};
/**
* List all of the groups with permissions on the specified library.
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Library Access Groups
* @namedParams
* @param {string} libraryId - The ID of the library* @param {string} libraryId - The ID of the library
* @param {(Array<string>)=} permissions - Limit permission types. If not specified, all permissions will be included
*
* @return {Promise<Object>} - Object mapping group addresses to permissions, as an array
* - Example: { "0x0": ["accessor", "contributor"], ...}
*/
exports.ContentLibraryGroupPermissions = async function({libraryId, permissions=[]}) {
ValidateLibrary(libraryId);
let libraryPermissions = {};
if(!permissions || permissions.length === 0) {
permissions = ["accessor", "contributor", "reviewer"];
} else {
// Format and validate specified permissions
permissions = permissions.map(permission => {
permission = permission.toLowerCase();
if(!["accessor", "contributor", "reviewer"].includes(permission)) {
throw Error(`Invalid permission: ${permission}`);
}
return permission;
});
}
this.Log(`Retrieving ${permissions.join(", ")} group(s) for library ${libraryId}`);
await Promise.all(
permissions.map(async type => {
// Get library access groups of the specified type
let numGroups = await this.CallContractMethod({
contractAddress: this.utils.HashToAddress(libraryId),
methodName: type + "GroupsLength"
});
numGroups = parseInt(numGroups._hex, 16);
const accessGroupAddresses = await this.utils.LimitedMap(
3,
[...Array(numGroups).keys()],
async i => {
try {
return this.utils.FormatAddress(
await this.CallContractMethod({
contractAddress: this.utils.HashToAddress(libraryId),
methodName: type + "Groups",
methodArgs: [i]
})
);
} catch(error) {
// eslint-disable-next-line no-console
console.error(error);
}
}
);
accessGroupAddresses.forEach(address =>
libraryPermissions[address] = [...(libraryPermissions[address] || []), type].sort());
})
);
return libraryPermissions;
};
/**
* Add accessor, contributor or reviewer permissions for the specified group on the specified library
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Library Access Groups
* @namedParams
* @param {string} libraryId - The ID of the library
* @param {string} groupAddress - The address of the group
* @param {string} permission - The type of permission to add ("accessor", "contributor", "reviewer")
*/
exports.AddContentLibraryGroup = async function({libraryId, groupAddress, permission}) {
ValidateLibrary(libraryId);
groupAddress = ValidateAddress(groupAddress);
if(!["accessor", "contributor", "reviewer"].includes(permission.toLowerCase())) {
throw Error(`Invalid group type: ${permission}`);
}
this.Log(`Adding ${permission} group ${groupAddress} to library ${libraryId}`);
const existingPermissions = await this.ContentLibraryGroupPermissions({
libraryId,
permissions: [permission]
});
if(existingPermissions[groupAddress]) { return; }
// Capitalize permission to match method and event names
permission = permission.charAt(0).toUpperCase() + permission.substr(1).toLowerCase();
const event = await this.CallContractMethodAndWait({
contractAddress: this.utils.HashToAddress(libraryId),
methodName: `add${permission}Group`,
methodArgs: [groupAddress]
});
const abi = await this.ContractAbi({id: libraryId});
await this.ExtractEventFromLogs({
abi,
event,
eventName: `${permission}GroupAdded`
});
};
/**
* Remove accessor, contributor or reviewer permissions for the specified group on the specified library
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Library Access Groups
* @namedParams
* @param {string} libraryId - The ID of the library
* @param {string} groupAddress - The address of the group
* @param {string} permission - The type of permission to remove ("accessor", "contributor", "reviewer")
*/
exports.RemoveContentLibraryGroup = async function({libraryId, groupAddress, permission}) {
ValidateLibrary(libraryId);
groupAddress = ValidateAddress(groupAddress);
if(!["accessor", "contributor", "reviewer"].includes(permission.toLowerCase())) {
throw Error(`Invalid group type: ${permission}`);
}
this.Log(`Removing ${permission} group ${groupAddress} from library ${libraryId}`);
const existingPermissions = await this.ContentLibraryGroupPermissions({
libraryId,
permissions: [permission]
});
if(!existingPermissions[groupAddress]) { return; }
// Capitalize permission to match method and event names
permission = permission.charAt(0).toUpperCase() + permission.substr(1).toLowerCase();
const event = await this.CallContractMethodAndWait({
contractAddress: this.utils.HashToAddress(libraryId),
methodName: `remove${permission}Group`,
methodArgs: [groupAddress]
});
const abi = await this.ContractAbi({id: libraryId});
await this.ExtractEventFromLogs({
abi,
event,
eventName: `${permission}GroupRemoved`
});
};
/**
* List all of the groups with permissions on the specified object or content type
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Object Access Groups
* @namedParams
* @param {string} objectId - The ID of the object
*
* @return {Promise<Object>} - Object mapping group addresses to permissions, as an array
* - Example: { "0x0": ["see", "access", "manage"], ...}
*/
exports.ContentObjectGroupPermissions = async function({objectId}) {
ValidateObject(objectId);
this.Log(`Retrieving group permissions for object ${objectId}`);
const contractAddress = this.utils.HashToAddress(objectId);
// Access indexor only available on access groups, so must ask each access group
// we belong to about this object
const groupAddresses = await this.Collection({collectionType: "accessGroups"});
let rightsMethod;
switch(await this.AccessType({id: objectId})) {
case this.authClient.ACCESS_TYPES.OBJECT:
rightsMethod = "getContentObjectRights";
break;
case this.authClient.ACCESS_TYPES.TYPE:
rightsMethod = "getContentTypeRights";
break;
case this.authClient.ACCESS_TYPES.GROUP:
rightsMethod = "getAccessGroupRights";
break;
case this.authClient.ACCESS_TYPES.LIBRARY:
rightsMethod = "getLibraryRights";
}
const groupPermissions = {};
await Promise.all(
groupAddresses.map(async groupAddress => {
try {
groupAddress = this.utils.FormatAddress(groupAddress);
let permission = await this.CallContractMethod({
contractAddress: groupAddress,
methodName: rightsMethod,
methodArgs: [contractAddress]
});
if(permission === 0) {
return;
}
let permissions = [];
if(permission >= 100) {
permissions.push("manage");
}
if(permission % 100 >= 10) {
permissions.push("access");
}
if(permission % 10 > 0) {
permissions.push("see");
}
groupPermissions[groupAddress] = permissions;
} catch(error) {
this.Log(`Failed to retrieve group permissions for ${groupAddress}`, true);
this.Log(error, true);
}
})
);
return groupPermissions;
};
/**
* Add a permission on the specified group for the specified object or content type
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Object Access Groups
* @namedParams
* @param {string} objectId - The ID of the object
* @param {string} groupAddress - The address of the group
* @param {string} permission - The type of permission to add ("see", "access", "manage")
*/
exports.AddContentObjectGroupPermission = async function({objectId, groupAddress, permission}) {
ValidatePresence("permission", permission);
ValidateObject(objectId);
groupAddress = ValidateAddress(groupAddress);
if(this.utils.EqualAddress(groupAddress, this.utils.HashToAddress(objectId))) {
throw Error("Group rights cannot be set on the same group");
}
permission = permission.toLowerCase();
if(!["see", "access", "manage"].includes(permission)) {
throw Error(`Invalid permission type: ${permission}`);
}
this.Log(`Adding ${permission} permission to group ${groupAddress} for ${objectId}`);
return await this.SetGroupPermission({
groupAddress,
objectId,
permission
});
};
/**
* Remove a permission on the specified group for the specified object or content type
*
* @memberof module:ElvClient/AccessGroups
* @methodGroup Object Access Groups
* @namedParams
* @param {string} objectId - The ID of the object
* @param {string} groupAddress - The address of the group
* @param {string} permission - The type of permission to remove ("see", "access", "manage")
*/
exports.RemoveContentObjectGroupPermission = async function({objectId, groupAddress, permission}) {
ValidatePresence("permission", permission);
ValidateObject(objectId);
groupAddress = ValidateAddress(groupAddress);
permission = permission.toLowerCase();
if(!["see", "access", "manage"].includes(permission)) {
throw Error(`Invalid permission type: ${permission}`);
}
this.Log(`Removing ${permission} permission from group ${groupAddress} for ${objectId}`);
return await this.SetGroupPermission({
groupAddress,
objectId,
permission,
remove: true
});
};
/**
* Link the specified group to an OAuth provider with the specified credentials
*
* @param {string} groupAddress - The address of the group
* @param {string} kmsId - The ID of the KMS (or trust authority ID)
* @param {string | Object} oauthConfig - The configuration for the OAuth settings
*/
exports.LinkAccessGroupToOauth = async function({groupAddress, kmsId, oauthConfig}) {
ValidateAddress(groupAddress);
ValidatePresence("kmsId", kmsId);
ValidatePresence("oauthConfig", oauthConfig);
if(typeof oauthConfig === "string") {
oauthConfig = JSON.parse(oauthConfig);
}
const { publicKey } = await this.authClient.KMSInfo({kmsId});
const kmsKey = `eluv.jwtv.${kmsId}`;
const kmsConfig = await this.Crypto.EncryptConk(oauthConfig, publicKey);
const userKey = `eluv.jwtv.iusr${this.utils.AddressToHash(this.signer.address)}`;
const userConfig = await this.EncryptECIES({message: oauthConfig});
const objectId = this.utils.AddressToObjectId(groupAddress);
const writeToken = (await this.EditContentObject({libraryId: this.contentSpaceLibraryId, objectId})).write_token;
await this.ReplaceMetadata({
libraryId: this.contentSpaceLibraryId,
objectId,
writeToken,
metadataSubtree: kmsKey,
metadata: kmsConfig
});
await this.ReplaceMetadata({
libraryId: this.contentSpaceLibraryId,
objectId,
writeToken,
metadataSubtree: userKey,
metadata: userConfig
});
await this.FinalizeContentObject({libraryId: this.contentSpaceLibraryId, objectId, writeToken, commitMessage: "Bind access group to OAuth"});
await this.CallContractMethodAndWait({
contractAddress: groupAddress,
methodName: "setOAuthEnabled",
methodArgs: [false]
});
await this.CallContractMethodAndWait({
contractAddress: groupAddress,
methodName: "setOAuthEnabled",
methodArgs: [true]
});
};
/**
* Disable the OAuth linking on the specified access group
*
* @param {string} groupAddress - The address of the group
*/
exports.UnlinkAccessGroupFromOauth = async function({groupAddress}) {
ValidateAddress(groupAddress);
await this.CallContractMethodAndWait({
contractAddress: groupAddress,
methodName: "setOAuthEnabled",
methodArgs: [false]
});
};