diff --git a/conf/db/zsv/V5.1.0__schema.sql b/conf/db/zsv/V5.1.0__schema.sql index 0c2187e60bb..e5d5edd3425 100644 --- a/conf/db/zsv/V5.1.0__schema.sql +++ b/conf/db/zsv/V5.1.0__schema.sql @@ -1,12 +1,32 @@ -CREATE TABLE IF NOT EXISTS `zstack`.`TpmKeyBackupVO` ( - `uuid` char(32) NOT NULL UNIQUE, - `lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59', - PRIMARY KEY (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -DELETE FROM `EncryptedResourceKeyRefVO` - WHERE `resourceUuid` NOT IN (SELECT `uuid` FROM `ResourceVO`); -ALTER TABLE `EncryptedResourceKeyRefVO` - ADD CONSTRAINT `fkEncryptedResourceKeyRefResourceVO` FOREIGN KEY (`resourceUuid`) REFERENCES `ResourceVO`(`uuid`) - ON DELETE CASCADE; +CREATE TABLE IF NOT EXISTS `zstack`.`TpmKeyBackupVO` ( + `uuid` char(32) NOT NULL UNIQUE, + `lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59', + PRIMARY KEY (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DELETE FROM `EncryptedResourceKeyRefVO` + WHERE `resourceUuid` NOT IN (SELECT `uuid` FROM `ResourceVO`); +ALTER TABLE `EncryptedResourceKeyRefVO` + ADD CONSTRAINT `fkEncryptedResourceKeyRefResourceVO` FOREIGN KEY (`resourceUuid`) REFERENCES `ResourceVO`(`uuid`) + ON DELETE CASCADE; + +-- Feature: ZCenter Account | ZSV-12257 + +ALTER TABLE `zstack`.`AccountVO` + ADD COLUMN `source` varchar(32) NOT NULL DEFAULT 'Local' AFTER `type`; + +UPDATE `zstack`.`AccountVO` a +INNER JOIN `zstack`.`AccountThirdPartyAccountSourceRefVO` ref ON ref.accountUuid = a.uuid +INNER JOIN `zstack`.`LdapServerVO` ldap ON ldap.uuid = ref.accountSourceUuid +SET a.`source` = IF(ldap.serverType IN ('OpenLdap', 'WindowsAD'), ldap.serverType, 'WindowsAD'); + +UPDATE `zstack`.`AccountVO` a +INNER JOIN `zstack`.`AccountThirdPartyAccountSourceRefVO` ref ON ref.accountUuid = a.uuid +INNER JOIN `zstack`.`ThirdPartyAccountSourceVO` src ON src.uuid = ref.accountSourceUuid +SET a.`source` = src.type +WHERE src.type IN ('CAS', 'OAuth2'); + +UPDATE `zstack`.`AccountVO` +SET `type` = 'Normal' +WHERE `type` = 'ThirdParty'; diff --git a/header/src/main/java/org/zstack/header/identity/AccountInventory.java b/header/src/main/java/org/zstack/header/identity/AccountInventory.java index e1ee9f6b51c..1993585a981 100755 --- a/header/src/main/java/org/zstack/header/identity/AccountInventory.java +++ b/header/src/main/java/org/zstack/header/identity/AccountInventory.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.UUID; @Inventory(mappingVOClass = AccountVO.class) @ExpandedQueries({ @@ -23,6 +22,7 @@ public class AccountInventory { private String name; private String description; private String type; + private String source; private String state; private Timestamp createDate; private Timestamp lastOpDate; @@ -33,6 +33,7 @@ public static AccountInventory valueOf(AccountVO vo) { inv.setName(vo.getName()); inv.setDescription(vo.getDescription()); inv.setType(vo.getType().toString()); + inv.setSource(vo.getSource().toString()); inv.setState(vo.getState().toString()); inv.setCreateDate(vo.getCreateDate()); inv.setLastOpDate(vo.getLastOpDate()); @@ -55,6 +56,14 @@ public void setType(String type) { this.type = type; } + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + public String getDescription() { return description; } @@ -109,6 +118,7 @@ public static AccountInventory __example__() { account.setName("account1"); account.setDescription("account1-description"); account.setType(AccountType.Normal.toString()); + account.setSource(AccountSource.Local.toString()); account.setState(AccountState.Enabled.toString()); account.setCreateDate(new Timestamp(DocUtils.date)); account.setLastOpDate(new Timestamp(DocUtils.date)); diff --git a/header/src/main/java/org/zstack/header/identity/AccountInventoryDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/identity/AccountInventoryDoc_zh_cn.groovy index 13a01ed1d9d..8172c0b592c 100644 --- a/header/src/main/java/org/zstack/header/identity/AccountInventoryDoc_zh_cn.groovy +++ b/header/src/main/java/org/zstack/header/identity/AccountInventoryDoc_zh_cn.groovy @@ -30,6 +30,12 @@ doc { type "String" since "4.0.0" } + field { + name "source" + desc "账户来源,创建时确定且不可修改" + type "String" + since "5.1.0" + } field { name "state" desc "账户状态" diff --git a/header/src/main/java/org/zstack/header/identity/AccountSource.java b/header/src/main/java/org/zstack/header/identity/AccountSource.java new file mode 100644 index 00000000000..85bd0edd656 --- /dev/null +++ b/header/src/main/java/org/zstack/header/identity/AccountSource.java @@ -0,0 +1,25 @@ +package org.zstack.header.identity; + +import org.zstack.header.configuration.PythonClass; + +import javax.annotation.Nullable; + +/** + * Where an account was originally created. Immutable after creation (ZSV-12257). + */ +@PythonClass +public enum AccountSource { + Local, + OpenLdap, + WindowsAD, + CAS, + OAuth2, + ZCenter; + + public static AccountSource fromLdapServerTypeName(@Nullable String serverType) { + if (OpenLdap.name().equals(serverType)) { + return OpenLdap; + } + return WindowsAD; + } +} diff --git a/header/src/main/java/org/zstack/header/identity/AccountType.java b/header/src/main/java/org/zstack/header/identity/AccountType.java index 34c7c24054d..56747780307 100755 --- a/header/src/main/java/org/zstack/header/identity/AccountType.java +++ b/header/src/main/java/org/zstack/header/identity/AccountType.java @@ -6,5 +6,9 @@ public enum AccountType { SystemAdmin, Normal, + /** + * @deprecated Use {@link AccountType#Normal} with {@link AccountSource} instead (ZSV-12257). + */ + @Deprecated ThirdParty } diff --git a/header/src/main/java/org/zstack/header/identity/AccountVO.java b/header/src/main/java/org/zstack/header/identity/AccountVO.java index 36ff0552b32..d96a46c2d68 100755 --- a/header/src/main/java/org/zstack/header/identity/AccountVO.java +++ b/header/src/main/java/org/zstack/header/identity/AccountVO.java @@ -31,6 +31,10 @@ public class AccountVO extends ResourceVO { @Enumerated(EnumType.STRING) private AccountType type; + @Column(nullable = false, updatable = false) + @Enumerated(EnumType.STRING) + private AccountSource source = AccountSource.Local; + @Column @Enumerated(EnumType.STRING) private AccountState state; @@ -72,6 +76,14 @@ public void setType(AccountType type) { this.type = type; } + public AccountSource getSource() { + return source; + } + + public void setSource(AccountSource source) { + this.source = source; + } + public AccountState getState() { return state; } diff --git a/header/src/main/java/org/zstack/header/identity/AccountVO_.java b/header/src/main/java/org/zstack/header/identity/AccountVO_.java index b1a43cf5a40..cc589f13c67 100755 --- a/header/src/main/java/org/zstack/header/identity/AccountVO_.java +++ b/header/src/main/java/org/zstack/header/identity/AccountVO_.java @@ -12,6 +12,7 @@ public class AccountVO_ extends ResourceVO_ { public static volatile SingularAttribute description; public static volatile SingularAttribute password; public static volatile SingularAttribute type; + public static volatile SingularAttribute source; public static volatile SingularAttribute state; public static volatile SingularAttribute createDate; public static volatile SingularAttribute lastOpDate; diff --git a/header/src/main/java/org/zstack/header/identity/CreateAccountMsg.java b/header/src/main/java/org/zstack/header/identity/CreateAccountMsg.java index 98373b8489e..1559e828500 100644 --- a/header/src/main/java/org/zstack/header/identity/CreateAccountMsg.java +++ b/header/src/main/java/org/zstack/header/identity/CreateAccountMsg.java @@ -13,6 +13,7 @@ public class CreateAccountMsg extends NeedReplyMessage { @NoLogging private String password; private String type; + private String source = AccountSource.Local.toString(); private String description; private AccountState state; @@ -48,6 +49,14 @@ public void setType(String type) { this.type = type; } + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + public String getDescription() { return description; } diff --git a/identity/src/main/java/org/zstack/identity/Account.java b/identity/src/main/java/org/zstack/identity/Account.java index 9050bd2df8a..4ce30624c32 100755 --- a/identity/src/main/java/org/zstack/identity/Account.java +++ b/identity/src/main/java/org/zstack/identity/Account.java @@ -5,6 +5,8 @@ import org.zstack.header.identity.role.RoleAccountRefVO; import org.zstack.header.identity.role.RoleAccountRefVO_; +import java.util.Objects; + import static org.zstack.header.identity.AccountConstant.ALL_RESOURCES_READABLE_ROLE_UUID; import static org.zstack.header.identity.AccountConstant.SOD_AUDITOR_ROLE_UUID; import static org.zstack.header.identity.AccountConstant.SOD_SYSTEM_ADMIN_ROLE_UUID; @@ -33,6 +35,13 @@ static boolean isAdminPermission(String accountUuid) { .isExists(); } + static boolean isAdminPermission(AccountInventory account) { + if (account == null) { + return false; + } + return isAdmin(account.getUuid()) || Objects.equals(account.getType(), AccountType.SystemAdmin.toString()); + } + static boolean isAdmin(SessionInventory session) { return AccountConstant.isAdmin(session.getAccountUuid()); } diff --git a/identity/src/main/java/org/zstack/identity/AccountManagerImpl.java b/identity/src/main/java/org/zstack/identity/AccountManagerImpl.java index 53fba446638..4285d969ec7 100755 --- a/identity/src/main/java/org/zstack/identity/AccountManagerImpl.java +++ b/identity/src/main/java/org/zstack/identity/AccountManagerImpl.java @@ -110,6 +110,7 @@ protected void scripts() { vo.setName(AccountConstant.INITIAL_SYSTEM_ADMIN_NAME); vo.setPassword(AccountConstant.INITIAL_SYSTEM_ADMIN_PASSWORD); vo.setType(AccountType.SystemAdmin); + vo.setSource(AccountSource.Local); vo.setState(AccountState.Enabled); persist(vo); flush(); @@ -533,7 +534,21 @@ protected AccountInventory scripts() { vo.setName(msg.getName()); vo.setDescription(msg.getDescription()); vo.setPassword(msg.getPassword()); - vo.setType(msg.getType() != null ? AccountType.valueOf(msg.getType()) : AccountType.Normal); + AccountType accountType = msg.getType() != null ? AccountType.valueOf(msg.getType()) : AccountType.Normal; + if (accountType == AccountType.ThirdParty) { + throw operr("account type[ThirdParty] is deprecated; use Normal with account source instead") + .toException(); + } + vo.setType(accountType); + + try { + vo.setSource(AccountSource.valueOf(msg.getSource())); + } catch (RuntimeException e) { + throw operr("invalid account source[%s]", msg.getSource()) + .withOpaque("allowed.values", list(AccountSource.values())) + .toException(); + } + vo.setState(msg.getState() == null ? AccountState.Enabled : msg.getState()); persist(vo); reload(vo); @@ -1005,6 +1020,11 @@ private void validate(APIChangeAccountTypeMsg msg) { )); } + if (AccountType.ThirdParty.toString().equals(msg.getType())) { + throw new ApiMessageInterceptionException(argerr( + "account type[ThirdParty] is deprecated; use Normal with account source instead")); + } + if (!AccountType.SystemAdmin.toString().equals(msg.getType())) { throw new ApiMessageInterceptionException(argerr( "Only promoting to SystemAdmin is currently supported, got type[%s].", msg.getType() diff --git a/identity/src/main/java/org/zstack/identity/rbac/RBACManagerImpl.java b/identity/src/main/java/org/zstack/identity/rbac/RBACManagerImpl.java index 3e589bbe97f..0a6e52ac0dd 100755 --- a/identity/src/main/java/org/zstack/identity/rbac/RBACManagerImpl.java +++ b/identity/src/main/java/org/zstack/identity/rbac/RBACManagerImpl.java @@ -177,7 +177,7 @@ public void prepareDbInitialValue() { if (AccountConstant.OTHER_ROLE_UUID.equals(role.getUuid()) || AccountConstant.LEGACY_ROLE_UUID.equals(role.getUuid())) { List accountUuidList = Q.New(AccountVO.class) - .in(AccountVO_.type, list(AccountType.Normal, AccountType.ThirdParty)) + .eq(AccountVO_.type, AccountType.Normal) .select(AccountVO_.uuid) .listValues(); for (String accountUuid : accountUuidList) { diff --git a/plugin/account-import/src/main/java/org/zstack/identity/imports/header/ImportAccountSpec.java b/plugin/account-import/src/main/java/org/zstack/identity/imports/header/ImportAccountSpec.java index 74c6cbd2273..fa419e329ed 100644 --- a/plugin/account-import/src/main/java/org/zstack/identity/imports/header/ImportAccountSpec.java +++ b/plugin/account-import/src/main/java/org/zstack/identity/imports/header/ImportAccountSpec.java @@ -1,5 +1,6 @@ package org.zstack.identity.imports.header; +import org.zstack.header.identity.AccountSource; import org.zstack.identity.imports.entity.SyncCreatedAccountStrategy; import org.zstack.identity.imports.entity.SyncUpdateAccountStateStrategy; @@ -12,6 +13,7 @@ public class ImportAccountSpec { private String sourceUuid; private String sourceType; + private AccountSource accountSource; public List accountList = new ArrayList<>(); private boolean createIfNotExist = true; private SyncCreatedAccountStrategy syncCreateStrategy = SyncCreatedAccountStrategy.NoAction; @@ -33,6 +35,14 @@ public void setSourceType(String sourceType) { this.sourceType = sourceType; } + public AccountSource getAccountSource() { + return accountSource; + } + + public void setAccountSource(AccountSource accountSource) { + this.accountSource = accountSource; + } + public List getAccountList() { return accountList; } diff --git a/plugin/account-import/src/main/java/org/zstack/identity/imports/source/AbstractAccountSourceBase.java b/plugin/account-import/src/main/java/org/zstack/identity/imports/source/AbstractAccountSourceBase.java index eb8f4d8796c..b0b568e3393 100644 --- a/plugin/account-import/src/main/java/org/zstack/identity/imports/source/AbstractAccountSourceBase.java +++ b/plugin/account-import/src/main/java/org/zstack/identity/imports/source/AbstractAccountSourceBase.java @@ -19,6 +19,7 @@ import org.zstack.header.core.WhileCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.identity.AccountInventory; +import org.zstack.header.identity.AccountSource; import org.zstack.header.identity.AccountState; import org.zstack.header.identity.AccountType; import org.zstack.header.identity.AccountVO; @@ -188,6 +189,7 @@ private void buildContextsFromSpec() { for (ImportAccountItem accountSpec : spec.getAccountList()) { final ImportThirdPartyAccountContext context = new ImportThirdPartyAccountContext(); context.spec = accountSpec; + context.accountSource = spec.getAccountSource(); contexts.add(context); validContexts.add(context); } @@ -260,6 +262,7 @@ private void fillAccountIfExists() { AccountVO account = uuidExistingAccountMap.get(accountUuid); if (account != null) { context.account = AccountInventory.valueOf(account); + context.accountSource = spec.getAccountSource(); context.accountExisting = true; } } @@ -344,11 +347,25 @@ private void createAccount(ImportThirdPartyAccountContext context, WhileCompleti "account[uuid=%s] newlyCreate stateInAccountSource=%s stateUpdateTo=%s %s", accountUuid, stateInAccountSource, stateUpdateTo, stateMachine)); + AccountType accountType = context.spec.getAccountType() != null ? + context.spec.getAccountType() : AccountType.Normal; + if (accountType == AccountType.ThirdParty) { + accountType = AccountType.Normal; + } + if (context.accountSource == null) { + context.errorForAccountExecution = operr( + "account source is required when importing account[name=%s]", context.spec.getUsername()); + validContexts.remove(context); + whileCompletion.done(); + return; + } + CreateAccountMsg message = new CreateAccountMsg(); message.setUuid(accountUuid); message.setName(context.spec.getUsername()); message.setPassword(generatePassword()); - message.setType(context.spec.getAccountType().toString()); + message.setType(accountType.toString()); + message.setSource(context.accountSource.toString()); message.setState(stateUpdateTo); bus.makeTargetServiceIdByResourceUuid(message, AccountConstant.SERVICE_ID, accountUuid); @@ -793,26 +810,18 @@ public void run(FlowTrigger trigger, Map data) { @Override public void run(FlowTrigger trigger, Map data) { - long totalSize = Q.New(AccountThirdPartyAccountSourceRefVO.class) - .eq(AccountThirdPartyAccountSourceRefVO_.accountSourceUuid, self.getUuid()) - .count(); - - // All related accounts with "ThirdParty" type will be destroyed - SQL.New("select " + - "account.uuid " + - "from " + - "AccountThirdPartyAccountSourceRefVO ref, " + - "AccountVO account " + - "where " + - "ref.accountSourceUuid = :sourceUuid and " + - "ref.accountUuid = account.uuid and " + - "account.type = :accountType", - String.class) - .param("sourceUuid", self.getUuid()) - .param("accountType", AccountType.ThirdParty) - .limit(300) - .paginate(totalSize, (List accountUuids) -> accountUuidList.addAll(accountUuids)); - trigger.next(); + gatherAccountsForDestroySource(new ReturnValueCompletion>(trigger) { + @Override + public void success(List returnValue) { + accountUuidList.addAll(returnValue); + trigger.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + trigger.fail(errorCode); + } + }); } }).then(new NoRollbackFlow() { String __name__ = "destroy-source"; @@ -904,4 +913,28 @@ public void handle(ErrorCode errCode, Map data) { } protected abstract void destroySource(Completion completion); + + protected void gatherAccountsForDestroySource(ReturnValueCompletion> completion) { + long totalSize = Q.New(AccountThirdPartyAccountSourceRefVO.class) + .eq(AccountThirdPartyAccountSourceRefVO_.accountSourceUuid, self.getUuid()) + .count(); + + List results = new ArrayList<>((int) totalSize); + // Imported accounts (non-local source) bound to this source will be destroyed + SQL.New("select " + + "account.uuid " + + "from " + + "AccountThirdPartyAccountSourceRefVO ref, " + + "AccountVO account " + + "where " + + "ref.accountSourceUuid = :sourceUuid and " + + "ref.accountUuid = account.uuid and " + + "account.source != :localSource", + String.class) + .param("sourceUuid", self.getUuid()) + .param("localSource", AccountSource.Local) + .limit(300) + .paginate(totalSize, (List accountUuids) -> results.addAll(accountUuids)); + completion.success(results); + } } diff --git a/plugin/account-import/src/main/java/org/zstack/identity/imports/source/ImportThirdPartyAccountContext.java b/plugin/account-import/src/main/java/org/zstack/identity/imports/source/ImportThirdPartyAccountContext.java index 05d5951878c..1ad97c6862e 100644 --- a/plugin/account-import/src/main/java/org/zstack/identity/imports/source/ImportThirdPartyAccountContext.java +++ b/plugin/account-import/src/main/java/org/zstack/identity/imports/source/ImportThirdPartyAccountContext.java @@ -2,6 +2,7 @@ import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.identity.AccountInventory; +import org.zstack.header.identity.AccountSource; import org.zstack.identity.imports.entity.AccountThirdPartyAccountSourceRefInventory; import org.zstack.identity.imports.entity.AccountThirdPartyAccountSourceRefVO; import org.zstack.identity.imports.header.ImportAccountItem; @@ -12,6 +13,7 @@ */ public class ImportThirdPartyAccountContext { public AccountInventory account; + public AccountSource accountSource; public ImportAccountItem spec; public AccountThirdPartyAccountSourceRefVO ref; public boolean readyToCreateAccount; diff --git a/plugin/ldap/src/main/java/org/zstack/ldap/compute/LdapSyncHelper.java b/plugin/ldap/src/main/java/org/zstack/ldap/compute/LdapSyncHelper.java index 132b6ef98f9..c9080cfb1c6 100644 --- a/plugin/ldap/src/main/java/org/zstack/ldap/compute/LdapSyncHelper.java +++ b/plugin/ldap/src/main/java/org/zstack/ldap/compute/LdapSyncHelper.java @@ -22,7 +22,9 @@ import org.zstack.header.core.workflow.NoRollbackFlow; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.ErrorCodeList; +import org.zstack.header.identity.AccountSource; import org.zstack.header.identity.AccountType; +import org.zstack.ldap.entity.LdapServerType; import org.zstack.header.message.MessageReply; import org.zstack.identity.imports.AccountImportsConstant; import org.zstack.identity.imports.entity.AccountThirdPartyAccountSourceRefVO; @@ -86,6 +88,7 @@ public LdapSyncHelper(LdapSyncTaskSpec spec) { importSpec = new ImportAccountSpec(); importSpec.setSourceType(LdapConstant.LOGIN_TYPE); importSpec.setSourceUuid(spec.sourceUuid); + importSpec.setAccountSource(toAccountSource(taskSpec.getServerType())); importSpec.setSyncCreateStrategy(taskSpec.getCreateAccountStrategy()); importSpec.setSyncUpdateStrategy(SyncUpdateAccountStateStrategy.from(taskSpec.getCreateAccountStrategy())); importSpec.setCreateIfNotExist( @@ -195,6 +198,7 @@ private List splitMessageFromImportSpec() { ImportAccountSpec splitSpec = new ImportAccountSpec(); splitSpec.setSourceUuid(importSpec.getSourceUuid()); splitSpec.setSourceType(importSpec.getSourceType()); + splitSpec.setAccountSource(importSpec.getAccountSource()); splitSpec.setAccountList(importSpec.getAccountList().subList(count, toIndexExclude)); splitSpec.setSyncCreateStrategy(importSpec.getSyncCreateStrategy()); splitSpec.setSyncUpdateStrategy(importSpec.getSyncUpdateStrategy()); @@ -364,12 +368,16 @@ private ImportAccountItem generateAccountSpec(LdapEntryInventory ldapEntry) { } account.setCredentials(dn); - account.setAccountType(AccountType.ThirdParty); + account.setAccountType(AccountType.Normal); account.setUsername(username); account.setEnable(ldapEntry.isEnable()); return account; } + private AccountSource toAccountSource(LdapServerType serverType) { + return AccountSource.fromLdapServerTypeName(serverType == null ? null : serverType.name()); + } + private String buildFilter() { AndFilter filter = new AndFilter(); if (taskSpec.getFilter() != null) { diff --git a/sdk/src/main/java/org/zstack/sdk/AccountInventory.java b/sdk/src/main/java/org/zstack/sdk/AccountInventory.java index 0a4e9a5d4c4..4c2ab997bd6 100644 --- a/sdk/src/main/java/org/zstack/sdk/AccountInventory.java +++ b/sdk/src/main/java/org/zstack/sdk/AccountInventory.java @@ -36,6 +36,14 @@ public java.lang.String getType() { return this.type; } + public java.lang.String source; + public void setSource(java.lang.String source) { + this.source = source; + } + public java.lang.String getSource() { + return this.source; + } + public java.lang.String state; public void setState(java.lang.String state) { this.state = state;