From ccd47c1b215cb2fad8c528d64cee7589d0163183 Mon Sep 17 00:00:00 2001 From: alena Date: Mon, 26 Sep 2011 10:07:54 -0700 Subject: [PATCH] Implemented Project Invitations --- api/src/com/cloud/acl/SecurityChecker.java | 5 +- api/src/com/cloud/api/ApiConstants.java | 3 + api/src/com/cloud/api/ResponseGenerator.java | 8 + .../api/commands/AddAccountToProjectCmd.java | 89 ++++ .../cloud/api/commands/CreateProjectCmd.java | 15 +- .../commands/DeleteAccountFromProjectCmd.java | 87 ++++ .../cloud/api/commands/DeleteProjectCmd.java | 11 +- .../cloud/api/commands/JoinProjectCmd.java | 82 ++++ .../api/commands/ListProjectAccountsCmd.java | 93 +++++ .../commands/ListProjectInvitationsCmd.java | 101 +++++ .../cloud/api/commands/UpdateProjectCmd.java | 97 +++++ .../api/response/ProjectAccountResponse.java | 90 ++++ .../response/ProjectInvitationResponse.java | 52 +++ .../cloud/api/response/ProjectResponse.java | 6 +- .../com/cloud/projects/ProjectAccount.java | 2 - .../com/cloud/projects/ProjectInvitation.java | 24 ++ .../com/cloud/projects/ProjectService.java | 12 + client/tomcatconf/commands.properties.in | 7 + server/src/com/cloud/acl/DomainChecker.java | 28 +- server/src/com/cloud/api/ApiDispatcher.java | 10 +- .../src/com/cloud/api/ApiResponseHelper.java | 108 +++-- .../baremetal/BareMetalVmManagerImpl.java | 4 +- .../src/com/cloud/configuration/Config.java | 5 +- .../DefaultComponentLibrary.java | 4 +- .../com/cloud/network/NetworkManagerImpl.java | 4 +- .../security/SecurityGroupManagerImpl.java | 2 +- .../com/cloud/projects/ProjectAccountVO.java | 12 +- .../cloud/projects/ProjectInvitationVO.java | 109 +++++ .../com/cloud/projects/ProjectManager.java | 6 + .../cloud/projects/ProjectManagerImpl.java | 389 +++++++++++++++++- server/src/com/cloud/projects/ProjectVO.java | 2 +- .../cloud/projects/dao/ProjectAccountDao.java | 20 + .../projects/dao/ProjectAccountDaoImpl.java | 46 ++- .../com/cloud/projects/dao/ProjectDao.java | 17 + .../projects/dao/ProjectInvitationDao.java | 30 ++ .../dao/ProjectInvitationDaoImpl.java | 99 +++++ .../ResourceLimitManagerImpl.java | 8 +- .../cloud/server/ManagementServerImpl.java | 8 +- server/src/com/cloud/user/AccountManager.java | 2 +- .../com/cloud/user/AccountManagerImpl.java | 14 +- .../src/com/cloud/user/DomainManagerImpl.java | 4 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 8 +- .../cloud/user/MockAccountManagerImpl.java | 2 +- setup/db/create-schema.sql | 26 +- setup/db/db/schema-2212to30.sql | 26 +- 45 files changed, 1660 insertions(+), 117 deletions(-) create mode 100644 api/src/com/cloud/api/commands/AddAccountToProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/DeleteAccountFromProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/JoinProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/ListProjectAccountsCmd.java create mode 100644 api/src/com/cloud/api/commands/ListProjectInvitationsCmd.java create mode 100644 api/src/com/cloud/api/commands/UpdateProjectCmd.java create mode 100644 api/src/com/cloud/api/response/ProjectAccountResponse.java create mode 100644 api/src/com/cloud/api/response/ProjectInvitationResponse.java create mode 100644 api/src/com/cloud/projects/ProjectInvitation.java create mode 100644 server/src/com/cloud/projects/ProjectInvitationVO.java create mode 100644 server/src/com/cloud/projects/dao/ProjectInvitationDao.java create mode 100644 server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java diff --git a/api/src/com/cloud/acl/SecurityChecker.java b/api/src/com/cloud/acl/SecurityChecker.java index 7a150c01337..ae6736a01a0 100644 --- a/api/src/com/cloud/acl/SecurityChecker.java +++ b/api/src/com/cloud/acl/SecurityChecker.java @@ -21,6 +21,7 @@ */ package com.cloud.acl; +import com.cloud.acl.SecurityChecker.AccessType; import com.cloud.dc.DataCenter; import com.cloud.domain.Domain; import com.cloud.exception.PermissionDeniedException; @@ -39,16 +40,18 @@ public interface SecurityChecker extends Adapter { public enum AccessType { ListEntry, ModifyEntry, + ModifyProject } /** * Checks if the account owns the object. * * @param caller account to check against. + * @param accessType TODO * @param object object that the account is trying to access. * @return true if access allowed. false if this adapter cannot authenticate ownership. * @throws PermissionDeniedException if this adapter is suppose to authenticate ownership and the check failed. */ - boolean checkAccess(Account caller, Domain domain) throws PermissionDeniedException; + boolean checkAccess(Account caller, Domain domain, AccessType accessType) throws PermissionDeniedException; /** * Checks if the user belongs to an account that owns the object. diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java index 5c63e42be3b..f25ecda6760 100755 --- a/api/src/com/cloud/api/ApiConstants.java +++ b/api/src/com/cloud/api/ApiConstants.java @@ -262,5 +262,8 @@ public class ApiConstants { public static final String MAX_GUESTS_LIMIT = "maxguestslimit"; public static final String PROJECT_ID = "projectid"; public static final String PROJECT = "project"; + public static final String ROLE = "role"; + public static final String USER = "user"; + public static final String ACTIVE_ONLY = "activeonly"; } diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java index 899a710e40a..8724bd87b0f 100755 --- a/api/src/com/cloud/api/ResponseGenerator.java +++ b/api/src/com/cloud/api/ResponseGenerator.java @@ -44,6 +44,8 @@ import com.cloud.api.response.LoadBalancerResponse; import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; import com.cloud.api.response.PodResponse; +import com.cloud.api.response.ProjectAccountResponse; +import com.cloud.api.response.ProjectInvitationResponse; import com.cloud.api.response.ProjectResponse; import com.cloud.api.response.RemoteAccessVpnResponse; import com.cloud.api.response.ResourceCountResponse; @@ -92,6 +94,8 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.projects.Project; +import com.cloud.projects.ProjectAccount; +import com.cloud.projects.ProjectInvitation; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; @@ -221,5 +225,9 @@ public interface ResponseGenerator { FirewallResponse createFirewallResponse(FirewallRule fwRule); HypervisorCapabilitiesResponse createHypervisorCapabilitiesResponse(HypervisorCapabilities hpvCapabilities); + + ProjectAccountResponse createProjectAccountResponse(ProjectAccount projectAccount); + + ProjectInvitationResponse createProjectInvitationResponse(ProjectInvitation invite); } diff --git a/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java b/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java new file mode 100644 index 00000000000..5f70c54175a --- /dev/null +++ b/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.user.Account; + +@Implementation(description="Adds acoount to a project", responseObject=SuccessResponse.class) +public class AddAccountToProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddAccountToProjectCmd.class.getName()); + + private static final String s_name = "addaccounttoprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, required=true, description="id of the project to add the account to") + private Long projectId; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, description="name of the account to be added to the project") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + boolean result = _projectService.addAccountToProject(getProjectId(), getAccountName()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add account to the project"); + } + } + + @Override + public long getEntityOwnerId() { + //TODO - return project entity ownerId + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/CreateProjectCmd.java b/api/src/com/cloud/api/commands/CreateProjectCmd.java index c0cf3877c50..8ccfef581aa 100644 --- a/api/src/com/cloud/api/commands/CreateProjectCmd.java +++ b/api/src/com/cloud/api/commands/CreateProjectCmd.java @@ -80,20 +80,7 @@ public class CreateProjectCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Account account = UserContext.current().getCaller(); - if ((account == null) || isAdmin(account.getType())) { - if ((domainId != null) && (accountName != null)) { - Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); - if (userAccount != null) { - return userAccount.getId(); - } - } - } - - if (account != null) { - return account.getId(); - } - + //TODO - return project entity ownerId return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked } diff --git a/api/src/com/cloud/api/commands/DeleteAccountFromProjectCmd.java b/api/src/com/cloud/api/commands/DeleteAccountFromProjectCmd.java new file mode 100644 index 00000000000..1e87a3f4a92 --- /dev/null +++ b/api/src/com/cloud/api/commands/DeleteAccountFromProjectCmd.java @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.user.Account; + +@Implementation(description="Deletes account from the project", responseObject=SuccessResponse.class) +public class DeleteAccountFromProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteProjectCmd.class.getName()); + + private static final String s_name = "deleteaccountfromprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, required=true, description="id of the project to remove the account from") + private Long projectId; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, description="name of the account to be removed from the project") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public Long getProjectId() { + return projectId; + } + + public String getAccountName() { + return accountName; + } + + @Override + public void execute(){ + boolean result = _projectService.deleteAccountFromProject(projectId, accountName); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete account from the project"); + } + } + + + @Override + public long getEntityOwnerId() { + //TODO - return project entity ownerId + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/DeleteProjectCmd.java b/api/src/com/cloud/api/commands/DeleteProjectCmd.java index 7d427436d38..e92a010a910 100644 --- a/api/src/com/cloud/api/commands/DeleteProjectCmd.java +++ b/api/src/com/cloud/api/commands/DeleteProjectCmd.java @@ -28,8 +28,7 @@ import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.SuccessResponse; import com.cloud.event.EventTypes; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.projects.Project; +import com.cloud.user.Account; import com.cloud.user.UserContext; @Implementation(description="Deletes a project", responseObject=SuccessResponse.class) @@ -87,11 +86,7 @@ public class DeleteProjectCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Project project = _projectService.getProject(id); - if (project == null) { - throw new InvalidParameterValueException("Project id=" + id + " doesn't exist"); - } else { - return _projectService.getProject(id).getProjectAccountId(); - } + //TODO - return project entity ownerId + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked } } \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/JoinProjectCmd.java b/api/src/com/cloud/api/commands/JoinProjectCmd.java new file mode 100644 index 00000000000..9ee441b7568 --- /dev/null +++ b/api/src/com/cloud/api/commands/JoinProjectCmd.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.user.Account; + +@Implementation(description="Makes account to join the project", responseObject=SuccessResponse.class) +public class JoinProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(JoinProjectCmd.class.getName()); + private static final String s_name = "joinprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PROJECT_ID, required=true, type=CommandType.LONG, description="list by project id") + private Long projectId; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list invitations for specified account; this parameter has to be specified with domainId") + private String accountName; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getProjectId() { + return projectId; + } + + public String getAccountName() { + return accountName; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + //TODO - return project entity ownerId + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + + @Override + public void execute(){ + boolean result = _projectService.joinProject(projectId, accountName); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to join the project"); + } + } +} diff --git a/api/src/com/cloud/api/commands/ListProjectAccountsCmd.java b/api/src/com/cloud/api/commands/ListProjectAccountsCmd.java new file mode 100644 index 00000000000..2a8ab6c91f4 --- /dev/null +++ b/api/src/com/cloud/api/commands/ListProjectAccountsCmd.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.ProjectAccountResponse; +import com.cloud.api.response.ProjectResponse; +import com.cloud.projects.ProjectAccount; +import com.cloud.user.Account; + +@Implementation(description="Lists project's accounts", responseObject=ProjectResponse.class) +public class ListProjectAccountsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListProjectAccountsCmd.class.getName()); + + private static final String s_name = "listprojectaccountsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, required=true, description="id of the project") + private Long projectId; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list accounts of the project by account name") + private String accountName; + + @Parameter(name=ApiConstants.ROLE, type=CommandType.STRING, description="list accounts of the project by role") + private String role; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + //TODO - return project entity ownerId + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + List projectAccounts = _projectService.listProjectAccounts(projectId, accountName, role, this.getStartIndex(), this.getPageSizeVal()); + ListResponse response = new ListResponse(); + List projectResponses = new ArrayList(); + for (ProjectAccount projectAccount : projectAccounts) { + ProjectAccountResponse projectAccountResponse = _responseGenerator.createProjectAccountResponse(projectAccount); + projectResponses.add(projectAccountResponse); + } + response.setResponses(projectResponses); + response.setResponseName(getCommandName()); + + this.setResponseObject(response); + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/ListProjectInvitationsCmd.java b/api/src/com/cloud/api/commands/ListProjectInvitationsCmd.java new file mode 100644 index 00000000000..e5599e74a2a --- /dev/null +++ b/api/src/com/cloud/api/commands/ListProjectInvitationsCmd.java @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.ProjectInvitationResponse; +import com.cloud.projects.ProjectInvitation; + +@Implementation(description="Lists projects and provides detailed information for listed projects", responseObject=ProjectInvitationResponse.class) +public class ListProjectInvitationsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListProjectInvitationsCmd.class.getName()); + private static final String s_name = "listprojectinvitationsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="list by project id") + private Long projectId; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list invitations for specified account; this parameter has to be specified with domainId") + private String accountName; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="list all invitations in specified domain") + private Long domainId; + + @Parameter(name=ApiConstants.ACTIVE_ONLY, type=CommandType.BOOLEAN, description="if true, list only active invitations - having Pending state and ones that are not timed out yet") + private boolean activeOnly; + + @Parameter(name=ApiConstants.STATE, type=CommandType.STRING, description="list invitations by state") + private String state; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getProjectId() { + return projectId; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public boolean isActiveOnly() { + return activeOnly; + } + + public String getState() { + return state; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + List invites = _projectService.listProjectInvitations(projectId, accountName, domainId, state, activeOnly, this.getStartIndex(), this.getPageSizeVal()); + ListResponse response = new ListResponse(); + List projectInvitationResponses = new ArrayList(); + for (ProjectInvitation invite : invites) { + ProjectInvitationResponse projectResponse = _responseGenerator.createProjectInvitationResponse(invite); + projectInvitationResponses.add(projectResponse); + } + response.setResponses(projectInvitationResponses); + response.setResponseName(getCommandName()); + + this.setResponseObject(response); + } +} diff --git a/api/src/com/cloud/api/commands/UpdateProjectCmd.java b/api/src/com/cloud/api/commands/UpdateProjectCmd.java new file mode 100644 index 00000000000..75524649236 --- /dev/null +++ b/api/src/com/cloud/api/commands/UpdateProjectCmd.java @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ProjectResponse; +import com.cloud.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@Implementation(description="Updates a project", responseObject=ProjectResponse.class) +public class UpdateProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreateProjectCmd.class.getName()); + + private static final String s_name = "updateprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="id of the project to be modified") + private Long id; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="new account who will own the project, should be specified with domainId") + private String accountName; + + @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, description="display text of the project") + private String displayText; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public Long getId() { + return id; + } + + public String getDisplayText() { + return displayText; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + //TODO - return project entity ownerId + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + UserContext.current().setEventDetails("Project id: "+ getId()); + Project project = _projectService.updateProject(getId(), getDisplayText(), getAccountName()); + if (project != null) { + ProjectResponse response = _responseGenerator.createProjectResponse(project); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to update a project"); + } + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/response/ProjectAccountResponse.java b/api/src/com/cloud/api/response/ProjectAccountResponse.java new file mode 100644 index 00000000000..f3708851f84 --- /dev/null +++ b/api/src/com/cloud/api/response/ProjectAccountResponse.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api.response; + +import java.util.List; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@SuppressWarnings("unused") +public class ProjectAccountResponse extends BaseResponse implements ControlledEntityResponse { + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="project id") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="project name") + private String projectName; + + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the id of the account") + private Long id; + + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the name of the account") + private String accountName; + + @SerializedName(ApiConstants.ACCOUNT_TYPE) @Param(description="account type (admin, domain-admin, user)") + private Short accountType; + + @SerializedName(ApiConstants.ROLE) @Param(description="account role in the project (regular,owner)") + private String role; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="id of the Domain the account belongs too") + private Long domainId; + + @SerializedName(ApiConstants.DOMAIN) @Param(description="name of the Domain the account belongs too") + private String domainName; + + @SerializedName(ApiConstants.USER) @Param(description="the list of users associated with account", responseObject = UserResponse.class) + private List users; + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setId(Long id) { + this.id = id; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setAccountType(Short accountType) { + this.accountType = accountType; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setUsers(List users) { + this.users = users; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/api/src/com/cloud/api/response/ProjectInvitationResponse.java b/api/src/com/cloud/api/response/ProjectInvitationResponse.java new file mode 100644 index 00000000000..d49e2df089c --- /dev/null +++ b/api/src/com/cloud/api/response/ProjectInvitationResponse.java @@ -0,0 +1,52 @@ +package com.cloud.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@SuppressWarnings("unused") +public class ProjectInvitationResponse extends BaseResponse implements ControlledEntityResponse{ + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the id of the project") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the name of the project") + private String projectName; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id the project belongs to") + private Long domainId; + + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name where the project belongs to") + private String domain; + + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account name of the project's owner") + private String accountName; + + @SerializedName(ApiConstants.STATE) @Param(description="the invitation state") + private String invitationState; + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domain) { + this.domain = domain; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setInvitationState(String invitationState) { + this.invitationState = invitationState; + } + + +} diff --git a/api/src/com/cloud/api/response/ProjectResponse.java b/api/src/com/cloud/api/response/ProjectResponse.java index 727f9d2d684..323a98bb678 100644 --- a/api/src/com/cloud/api/response/ProjectResponse.java +++ b/api/src/com/cloud/api/response/ProjectResponse.java @@ -25,13 +25,13 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") public class ProjectResponse extends BaseResponse{ - @SerializedName("id") @Param(description="the id of the project") + @SerializedName(ApiConstants.ID) @Param(description="the id of the project") private Long id; - @SerializedName("name") @Param(description="the name of the project") + @SerializedName(ApiConstants.NAME) @Param(description="the name of the project") private String name; - @SerializedName("displaytext") @Param(description="the displaytext of the project") + @SerializedName(ApiConstants.DISPLAY_TEXT) @Param(description="the displaytext of the project") private String displaytext; @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id the project belongs to") diff --git a/api/src/com/cloud/projects/ProjectAccount.java b/api/src/com/cloud/projects/ProjectAccount.java index 60cd145e4d3..11918dcc331 100644 --- a/api/src/com/cloud/projects/ProjectAccount.java +++ b/api/src/com/cloud/projects/ProjectAccount.java @@ -9,8 +9,6 @@ public interface ProjectAccount { long getProjectId(); Role getAccountRole(); - - long getId(); long getProjectAccountId(); diff --git a/api/src/com/cloud/projects/ProjectInvitation.java b/api/src/com/cloud/projects/ProjectInvitation.java new file mode 100644 index 00000000000..954891deeab --- /dev/null +++ b/api/src/com/cloud/projects/ProjectInvitation.java @@ -0,0 +1,24 @@ +package com.cloud.projects; + +import java.util.Date; + +public interface ProjectInvitation { + public enum State {Pending, Completed, Expired} + + long getId(); + + long getProjectId(); + + Long getAccountId(); + + String getToken(); + + String getEmail(); + + Date getCreated(); + + State getState(); + + Long getDomainId(); + +} diff --git a/api/src/com/cloud/projects/ProjectService.java b/api/src/com/cloud/projects/ProjectService.java index e94351aef9e..c46ca0b0271 100644 --- a/api/src/com/cloud/projects/ProjectService.java +++ b/api/src/com/cloud/projects/ProjectService.java @@ -49,4 +49,16 @@ public interface ProjectService { Project findByProjectAccountId(long projectAccountId); Project findByNameAndDomainId(String name, long domainId); + + Project updateProject(long id, String displayText, String newOwnerName); + + boolean addAccountToProject(long projectId, String accountName); + + boolean deleteAccountFromProject(long projectId, String accountName); + + List listProjectAccounts(long projectId, String accountName, String role, Long startIndex, Long pageSizeVal); + + List listProjectInvitations(Long projectId, String accountName, Long domainId, String state, boolean activeOnly, Long startIndex, Long pageSizeVal); + + boolean joinProject(long projectId, String accountName); } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 645a02ed729..0572f339725 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -267,7 +267,14 @@ listSSHKeyPairs=com.cloud.api.commands.ListSSHKeyPairsCmd;15 #### Projects commands createProject=com.cloud.api.commands.CreateProjectCmd;15 deleteProject=com.cloud.api.commands.DeleteProjectCmd;15 +updateProject=com.cloud.api.commands.UpdateProjectCmd;15 listProjects=com.cloud.api.commands.ListProjectsCmd;15 +addAccountToProject=com.cloud.api.commands.AddAccountToProjectCmd;15 +deleteAccountFromProject=com.cloud.api.commands.DeleteAccountFromProjectCmd;15 +listProjectAccounts=com.cloud.api.commands.ListProjectAccountsCmd;15 +listProjectInvitations=com.cloud.api.commands.ListProjectInvitationsCmd;15 + +joinProject=com.cloud.api.commands.JoinProjectCmd;15 #### createFirewallRule=com.cloud.api.commands.CreateFirewallRuleCmd;15 diff --git a/server/src/com/cloud/acl/DomainChecker.java b/server/src/com/cloud/acl/DomainChecker.java index b73e1d26ee4..84b9273816d 100755 --- a/server/src/com/cloud/acl/DomainChecker.java +++ b/server/src/com/cloud/acl/DomainChecker.java @@ -29,6 +29,7 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.projects.Project; import com.cloud.projects.ProjectManager; +import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.storage.LaunchPermissionVO; import com.cloud.storage.dao.LaunchPermissionDao; import com.cloud.template.VirtualMachineTemplate; @@ -45,29 +46,35 @@ public class DomainChecker extends AdapterBase implements SecurityChecker { @Inject AccountDao _accountDao; @Inject LaunchPermissionDao _launchPermissionDao; @Inject ProjectManager _projectMgr; + @Inject ProjectAccountDao _projecAccountDao; protected DomainChecker() { super(); } @Override - public boolean checkAccess(Account caller, Domain domain) throws PermissionDeniedException { + public boolean checkAccess(Account caller, Domain domain, AccessType accessType) throws PermissionDeniedException { if (caller.getState() != Account.State.enabled) { throw new PermissionDeniedException(caller + " is disabled."); } long domainId = domain.getId(); if (domain.getType() == Domain.Type.Project) { + if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { - if (!_projectMgr.canAccessDomain(caller, domainId)){ + if (accessType != null && accessType == AccessType.ModifyProject) { + if (!_projectMgr.canModifyProjectDomain(caller, domainId)) { + throw new PermissionDeniedException(caller + " does not have permission to operate within " + domain); + } + } else if (!_projectMgr.canAccessDomain(caller, domainId)){ throw new PermissionDeniedException(caller + " does not have permission to operate within " + domain); } return true; - } else { - //need to check the domain the project belongs to - Project project = _projectMgr.findByProjectDomainId(domainId); - domainId = project.getProjectDomainId(); } + + //need to check the domain the project belongs to + Project project = _projectMgr.findByProjectDomainId(domainId); + domainId = project.getDomainId(); } if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { @@ -87,7 +94,7 @@ public class DomainChecker extends AdapterBase implements SecurityChecker { throw new PermissionDeniedException(user + " is no longer active."); } Account account = _accountDao.findById(user.getAccountId()); - return checkAccess(account, domain); + return checkAccess(account, domain, null); } @Override @@ -123,7 +130,12 @@ public class DomainChecker extends AdapterBase implements SecurityChecker { Account account = _accountDao.findById(entity.getAccountId()); if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { - if (!_projectMgr.canAccessAccount(caller, account.getId())){ + //only project owner can delete/modify the project + if (accessType != null && accessType == AccessType.ModifyProject) { + if (!_projectMgr.canModifyProjectAccount(caller, account.getId())) { + throw new PermissionDeniedException(caller + " does not have permission to operate with resource " + entity); + } + } else if (!_projectMgr.canAccessAccount(caller, account.getId())){ throw new PermissionDeniedException(caller + " does not have permission to operate with resource " + entity); } } else { diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index ceb1f3172f3..a3f455d8d37 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -198,7 +198,7 @@ public class ApiDispatcher { Object paramObj = unpackedParams.get(parameterAnnotation.name()); if (paramObj == null) { if (parameterAnnotation.required()) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName() + " due to missing parameter " + parameterAnnotation.name()); + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length()-8) + " due to missing parameter " + parameterAnnotation.name()); } continue; } @@ -210,18 +210,18 @@ public class ApiDispatcher { if (s_logger.isDebugEnabled()) { s_logger.debug("Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " + parameterAnnotation.name()); } - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length()-8) + " due to invalid value " + paramObj + " for parameter " + parameterAnnotation.name()); } catch (ParseException parseEx) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName()); + s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName().substring(0, cmd.getCommandName().length()-8)); } - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " + cmd.getCommandName() + ", please pass dates in the format mentioned in the api documentation"); + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " + cmd.getCommandName().substring(0, cmd.getCommandName().length()-8) + ", please pass dates in the format mentioned in the api documentation"); } catch (CloudRuntimeException cloudEx) { // FIXME: Better error message? This only happens if the API command is not executable, which typically means // there was // and IllegalAccessException setting one of the parameters. - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName()); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length()-8)); } } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 8f490cbffa5..6ad1933269e 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -63,6 +63,8 @@ import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; import com.cloud.api.response.NicResponse; import com.cloud.api.response.PodResponse; +import com.cloud.api.response.ProjectAccountResponse; +import com.cloud.api.response.ProjectInvitationResponse; import com.cloud.api.response.ProjectResponse; import com.cloud.api.response.RemoteAccessVpnResponse; import com.cloud.api.response.ResourceCountResponse; @@ -132,6 +134,8 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.projects.Project; +import com.cloud.projects.ProjectAccount; +import com.cloud.projects.ProjectInvitation; import com.cloud.server.Criteria; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; @@ -177,27 +181,6 @@ public class ApiResponseHelper implements ResponseGenerator { public final Logger s_logger = Logger.getLogger(ApiResponseHelper.class); - @Override - public UserResponse createUserResponse(UserAccount user) { - UserResponse userResponse = new UserResponse(); - userResponse.setAccountName(user.getAccountName()); - userResponse.setAccountType(user.getType()); - userResponse.setCreated(user.getCreated()); - userResponse.setDomainId(user.getDomainId()); - userResponse.setDomainName(ApiDBUtils.findDomainById(user.getDomainId()).getName()); - userResponse.setEmail(user.getEmail()); - userResponse.setFirstname(user.getFirstname()); - userResponse.setId(user.getId()); - userResponse.setLastname(user.getLastname()); - userResponse.setState(user.getState()); - userResponse.setTimezone(user.getTimezone()); - userResponse.setUsername(user.getUsername()); - userResponse.setApiKey(user.getApiKey()); - userResponse.setSecretKey(user.getSecretKey()); - userResponse.setObjectName("user"); - - return userResponse; - } @Override public UserResponse createUserResponse(User user) { @@ -330,28 +313,36 @@ public class ApiResponseHelper implements ResponseGenerator { List usersForAccount = ApiDBUtils.listUsersByAccount(account.getAccountId()); List userResponseList = new ArrayList(); for (UserVO user : usersForAccount) { - UserResponse userResponse = new UserResponse(); - userResponse.setAccountName(account.getAccountName()); - userResponse.setAccountType(account.getType()); - userResponse.setApiKey(user.getApiKey()); - userResponse.setCreated(user.getCreated()); - userResponse.setDomainId(account.getDomainId()); - userResponse.setDomainName(ApiDBUtils.findDomainById(account.getDomainId()).getName()); - userResponse.setEmail(user.getEmail()); - userResponse.setFirstname(user.getFirstname()); - userResponse.setId(user.getId()); - userResponse.setSecretKey(user.getSecretKey()); - userResponse.setLastname(user.getLastname()); - userResponse.setState(user.getState().toString()); - userResponse.setTimezone(user.getTimezone()); - userResponse.setUsername(user.getUsername()); - + UserResponse userResponse = createUserResponse(user); userResponseList.add(userResponse); } accountResponse.setUsers(userResponseList); return accountResponse; } + + + @Override + public UserResponse createUserResponse(UserAccount user) { + UserResponse userResponse = new UserResponse(); + userResponse.setAccountName(user.getAccountName()); + userResponse.setAccountType(user.getType()); + userResponse.setCreated(user.getCreated()); + userResponse.setDomainId(user.getDomainId()); + userResponse.setDomainName(ApiDBUtils.findDomainById(user.getDomainId()).getName()); + userResponse.setEmail(user.getEmail()); + userResponse.setFirstname(user.getFirstname()); + userResponse.setId(user.getId()); + userResponse.setLastname(user.getLastname()); + userResponse.setState(user.getState()); + userResponse.setTimezone(user.getTimezone()); + userResponse.setUsername(user.getUsername()); + userResponse.setApiKey(user.getApiKey()); + userResponse.setSecretKey(user.getSecretKey()); + userResponse.setObjectName("user"); + + return userResponse; + } @Override public DomainResponse createDomainResponse(Domain domain) { @@ -2462,5 +2453,48 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDomainName(domain.getName()); } + @Override + public ProjectAccountResponse createProjectAccountResponse(ProjectAccount projectAccount) { + Account account = ApiDBUtils.findAccountById(projectAccount.getAccountId()); + ProjectAccountResponse projectAccountResponse = new ProjectAccountResponse(); + + long projectId = projectAccount.getProjectId(); + projectAccountResponse.setProjectId(projectId); + projectAccountResponse.setProjectName(ApiDBUtils.findProjectById(projectId).getName()); + + projectAccountResponse.setId(account.getId()); + projectAccountResponse.setAccountName(account.getAccountName()); + projectAccountResponse.setAccountType(account.getType()); + projectAccountResponse.setRole(projectAccount.getAccountRole().toString()); + populateDomain(projectAccountResponse, account.getDomainId()); + + // add all the users for an account as part of the response obj + List usersForAccount = ApiDBUtils.listUsersByAccount(account.getAccountId()); + List userResponseList = new ArrayList(); + for (UserVO user : usersForAccount) { + UserResponse userResponse = createUserResponse(user); + userResponseList.add(userResponse); + } + + projectAccountResponse.setUsers(userResponseList); + projectAccountResponse.setObjectName("projectaccount"); + + return projectAccountResponse; + } + + @Override + public ProjectInvitationResponse createProjectInvitationResponse(ProjectInvitation invite) { + ProjectInvitationResponse response = new ProjectInvitationResponse(); + response.setProjectId(invite.getProjectId()); + response.setProjectName(ApiDBUtils.findProjectById(invite.getProjectId()).getName()); + response.setInvitationState(invite.getState().toString()); + + Account account = ApiDBUtils.findAccountById(invite.getAccountId()); + response.setAccountName(account.getAccountName()); + populateDomain(response, account.getDomainId()); + + response.setObjectName("projectinvitation"); + return response; + } } diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java index 245a9ba6af9..ef1e7684e31 100755 --- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -242,8 +242,8 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet if (domain == null) { throw new CloudRuntimeException("Unable to find the domain " + dc.getDomainId() + " for the zone: " + dc); } - _accountMgr.checkAccess(caller, domain); - _accountMgr.checkAccess(owner, domain); + _accountMgr.checkAccess(caller, domain, null); + _accountMgr.checkAccess(owner, domain, null); } // check if account/domain is with in resource limits to create a new vm diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 79e5fb48b5f..6db801ebd08 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -287,7 +287,10 @@ public enum Config { DefaultMaxProjectPublicIPs("Project Defaults", ManagementServer.class, Long.class, "max.project.public.ips", "20", "The default maximum number of public IPs that can be consumed by a project", null), DefaultMaxProjectTemplates("Project Defaults", ManagementServer.class, Long.class, "max.project.templates", "20", "The default maximum number of templates that can be deployed for a project", null), DefaultMaxProjectSnapshots("Project Defaults", ManagementServer.class, Long.class, "max.project.snapshots", "20", "The default maximum number of snapshots that can be created for a project", null), - DefaultMaxProjectVolumes("Project Defaults", ManagementServer.class, Long.class, "max.project.volumes", "20", "The default maximum number of volumes that can be created for a project", null); + DefaultMaxProjectVolumes("Project Defaults", ManagementServer.class, Long.class, "max.project.volumes", "20", "The default maximum number of volumes that can be created for a project", null), + + ProjectInviteRequired("Project Defaults", ManagementServer.class, Boolean.class, "project.invite.required", "false", "If invitation confirmation is required when add account to project. Default value is false", null), + ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null); private final String _category; private final Class _componentClass; diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index c593b174145..0c299e2b290 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -107,8 +107,9 @@ import com.cloud.network.security.dao.VmRulesetLogDaoImpl; import com.cloud.network.vpn.RemoteAccessVpnManagerImpl; import com.cloud.offerings.dao.NetworkOfferingDaoImpl; import com.cloud.projects.ProjectManagerImpl; -import com.cloud.projects.dao.ProjectDaoImpl; import com.cloud.projects.dao.ProjectAccountDaoImpl; +import com.cloud.projects.dao.ProjectDaoImpl; +import com.cloud.projects.dao.ProjectInvitationDaoImpl; import com.cloud.resource.ResourceManagerImpl; import com.cloud.resourcelimit.ResourceLimitManagerImpl; import com.cloud.service.dao.ServiceOfferingDaoImpl; @@ -276,6 +277,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addDao("InlineLoadBalancerNicMapDao", InlineLoadBalancerNicMapDaoImpl.class); addDao("ElasticLbVmMap", ElasticLbVmMapDaoImpl.class); addDao("ProjectsAccountDao", ProjectAccountDaoImpl.class); + addDao("ProjectInvitationDao", ProjectInvitationDaoImpl.class); info = addDao("HypervisorCapabilitiesDao",HypervisorCapabilitiesDaoImpl.class); info.addParameter("cache.size", "100"); info.addParameter("cache.time.to.live", "600"); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 0e3201c4e4f..33d6f31c49b 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1562,7 +1562,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (domain == null) { throw new InvalidParameterValueException("Unable to find domain by id " + cmd.getDomainId()); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); isDomainSpecific = true; } } @@ -1858,7 +1858,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist in the system"); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountName != null) { Account owner = _accountMgr.getActiveAccountByName(accountName, domainId); if (owner == null) { diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index 507813158e0..9983c5fd328 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -1037,7 +1037,7 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG if (domain == null) { throw new InvalidParameterValueException("Unable to find domain by id " + domainId); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountName != null) { Account account = _accountMgr.getActiveAccountByName(accountName, domainId); if (account == null) { diff --git a/server/src/com/cloud/projects/ProjectAccountVO.java b/server/src/com/cloud/projects/ProjectAccountVO.java index 6cd2960a76f..c757025d10c 100644 --- a/server/src/com/cloud/projects/ProjectAccountVO.java +++ b/server/src/com/cloud/projects/ProjectAccountVO.java @@ -17,6 +17,8 @@ */ package com.cloud.projects; +import java.util.Date; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -26,6 +28,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name="project_account") public class ProjectAccountVO implements ProjectAccount{ @@ -49,6 +53,9 @@ public class ProjectAccountVO implements ProjectAccount{ @Column(name="project_domain_id") long projectDomainId; + + @Column(name=GenericDao.CREATED_COLUMN) + private Date created; protected ProjectAccountVO(){ @@ -62,7 +69,6 @@ public class ProjectAccountVO implements ProjectAccount{ this.projectDomainId = project.getProjectDomainId(); } - @Override public long getId() { return id; } @@ -91,4 +97,8 @@ public class ProjectAccountVO implements ProjectAccount{ public long getProjectDomainId() { return projectDomainId; } + + public void setAccountRole(Role accountRole) { + this.accountRole = accountRole; + } } diff --git a/server/src/com/cloud/projects/ProjectInvitationVO.java b/server/src/com/cloud/projects/ProjectInvitationVO.java new file mode 100644 index 00000000000..5d612ef342f --- /dev/null +++ b/server/src/com/cloud/projects/ProjectInvitationVO.java @@ -0,0 +1,109 @@ +package com.cloud.projects; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name="project_invitations") +public class ProjectInvitationVO implements ProjectInvitation{ + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="project_id") + private long projectId; + + @Column(name="account_id") + private Long accountId; + + @Column(name="domain_id") + private Long domainId; + + @Column(name="token") + private String token; + + @Column(name="email") + private String email; + + @Column(name="state") + @Enumerated(value=EnumType.STRING) + private State state = State.Pending; + + @Column(name=GenericDao.CREATED_COLUMN) + private Date created; + + + protected ProjectInvitationVO(){ + } + + public ProjectInvitationVO(long projectId, Long accountId, Long domainId, String email, String token) { + this.accountId = accountId; + this.domainId = domainId; + this.projectId = projectId; + this.email = email; + this.token = token; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getProjectId() { + return projectId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + @Override + public String getToken() { + return token; + } + + @Override + public String getEmail() { + return email; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder("ProjectInvitation["); + buf.append(id).append("|projectId=").append(projectId).append("|accountId=").append(accountId).append("]"); + return buf.toString(); + } + + @Override + public Long getDomainId() { + return domainId; + } + +} diff --git a/server/src/com/cloud/projects/ProjectManager.java b/server/src/com/cloud/projects/ProjectManager.java index 06a95d4e1d6..1cac7120e83 100644 --- a/server/src/com/cloud/projects/ProjectManager.java +++ b/server/src/com/cloud/projects/ProjectManager.java @@ -6,4 +6,10 @@ public interface ProjectManager extends ProjectService { boolean canAccessAccount(Account caller, long accountId); boolean canAccessDomain(Account caller, long domainId); + + boolean canModifyProjectAccount(Account caller, long accountId); + + boolean canModifyProjectDomain(Account caller, long domainId); + + boolean deleteAccountFromProject(long projectId, long accountId); } diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java index 90dc66a99e8..6233a91ce50 100644 --- a/server/src/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -1,5 +1,23 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ package com.cloud.projects; +import java.sql.Date; import java.util.List; import java.util.Map; @@ -8,8 +26,11 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import com.cloud.acl.SecurityChecker.AccessType; +import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -19,13 +40,17 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.projects.Project.State; +import com.cloud.projects.ProjectAccount.Role; import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; +import com.cloud.projects.dao.ProjectInvitationDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; import com.cloud.user.ResourceLimitService; import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; @@ -55,11 +80,24 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ ResourceLimitService _resourceLimitMgr; @Inject private ProjectAccountDao _projectAccountDao; + @Inject + private AccountDao _accountDao; + @Inject + private ConfigurationDao _configDao; + @Inject + private ProjectInvitationDao _projectInvitationDao; + + protected boolean _invitationRequired = false; + protected long _invitationTimeOut = 86400; @Override public boolean configure(final String name, final Map params) throws ConfigurationException { _name = name; + + Map configs = _configDao.getConfiguration(params); + _invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key())); + _invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key())); return true; } @@ -149,7 +187,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ throw new InvalidParameterValueException("Unable to find project by id " + projectId); } - _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId())); + _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId()), AccessType.ModifyProject); //mark project as inactive first, so you can't add resources to it Transaction txn = Transaction.currentTxn(); @@ -231,7 +269,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist in the system"); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountName != null) { Account owner = _accountMgr.getActiveAccountByName(accountName, domainId); @@ -301,6 +339,12 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ return _projectAccountDao.persist(new ProjectAccountVO(project, accountId, accountRole)); } + @Override + public boolean deleteAccountFromProject(long projectId, long accountId) { + ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, accountId); + return _projectAccountDao.remove(projectAccount.getId()); + } + @Override public Account getProjectOwner(long projectId) { long accountId = _projectAccountDao.getProjectOwner(projectId).getAccountId(); @@ -331,5 +375,346 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ public boolean canAccessDomain(Account caller, long domainId) { return _projectAccountDao.canAccessDomain(caller.getId(), domainId); } + + public boolean canModifyProjectAccount(Account caller, long accountId) { + return _projectAccountDao.canModifyProjectAccount(caller.getId(), accountId); + } + + @Override + public boolean canModifyProjectDomain(Account caller, long domainId) { + return _projectAccountDao.canModifyProjectDomain(caller.getId(), domainId); + } + + @Override @DB + public Project updateProject(long projectId, String displayText, String newOwnerName) { + Account caller = UserContext.current().getCaller(); + + //check that the project exists + ProjectVO project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getProjectDomainId()), AccessType.ModifyProject); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + if (displayText != null) { + project.setDisplayText(displayText); + _projectDao.update(projectId, project); + } + + if (newOwnerName != null) { + //check that the new owner exists + Account futureOwnerAccount = _accountMgr.getActiveAccountByName(newOwnerName, project.getDomainId()); + if (futureOwnerAccount == null) { + throw new InvalidParameterValueException("Unable to find account name=" + newOwnerName + " in domain id=" + project.getDomainId()); + } + Account currentOwnerAccount = getProjectOwner(projectId); + if (currentOwnerAccount.getId() != futureOwnerAccount.getId()) { + ProjectAccountVO futureOwner = _projectAccountDao.findByProjectIdAccountId(projectId, futureOwnerAccount.getAccountId()); + if (futureOwner == null) { + throw new InvalidParameterValueException("Account " + newOwnerName + " doesn't belong to the project. Add it to the project first and then change the project's ownership"); + } + + //unset the role for the old owner + ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); + currentOwner.setAccountRole(Role.Regular); + _projectAccountDao.update(currentOwner.getId(), currentOwner); + + //set new owner + futureOwner.setAccountRole(Role.Owner); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + + } else { + s_logger.trace("Future owner " + newOwnerName + "is already the owner of the project id=" + projectId); + } + } + + txn.commit(); + + return _projectDao.findById(projectId); + + } + + @Override + public boolean addAccountToProject(long projectId, String accountName) { + Account caller = UserContext.current().getCaller(); + + //check that the project exists + Project project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + //check that account-to-add exists + Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); + if (account == null) { + throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getProjectDomainId()), AccessType.ModifyProject); + + //Check if the account already added to the project + ProjectAccount projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId()); + if (projectAccount != null) { + s_logger.debug("Account " + accountName + " already added to the project id=" + projectId); + return true; + } + + if (_invitationRequired) { + //TODO - token based registration + if (generateInvitation(projectId, account.getId()) != null) { + return true; + } else { + s_logger.warn("Failed to generate invitation for account " + accountName + " to project id=" + projectId); + return false; + } + + } else { + if (assignAccountToProject(project, account.getId(), ProjectAccount.Role.Regular) != null) { + return true; + } else { + s_logger.warn("Failed to add account " + accountName + " to project id=" + projectId); + return false; + } + } + } + + @Override + public boolean deleteAccountFromProject(long projectId, String accountName) { + Account caller = UserContext.current().getCaller(); + + //check that the project exists + Project project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + //check that account-to-remove exists + Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); + if (account == null) { + throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getProjectDomainId()), AccessType.ModifyProject); + + //Check if the account exists in the project + ProjectAccount projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId()); + if (projectAccount == null) { + throw new InvalidParameterValueException("Account " + accountName + " is not assigned to the project id=" + projectId); + } + + //can't remove the owner of the project + if (projectAccount.getAccountRole() == Role.Owner) { + throw new InvalidParameterValueException("Unable to delete account " + accountName + " from the project id=" + projectId + " as the account is the owner of the project"); + } + + return deleteAccountFromProject(projectId, account.getId()); + } + + + @Override + public List listProjectAccounts(long projectId, String accountName, String role, Long startIndex, Long pageSizeVal) { + Account caller = UserContext.current().getCaller(); + + //check that the project exists + Project project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getProjectDomainId()), null); + + Filter searchFilter = new Filter(ProjectAccountVO.class, "id", false, startIndex, pageSizeVal); + SearchBuilder sb = _projectAccountDao.createSearchBuilder(); + sb.and("accountRole", sb.entity().getAccountRole(), Op.EQ); + + SearchBuilder accountSearch; + if (accountName != null) { + accountSearch = _accountDao.createSearchBuilder(); + accountSearch.and("accountName", accountSearch.entity().getAccountName(), SearchCriteria.Op.EQ); + sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER); + } + + SearchCriteria sc = sb.create(); + + if (role != null) { + sc.setParameters("accountRole", role); + } + + if (accountName != null) { + sc.setJoinParameters("accountSearch", "accountName", accountName); + } + + return _projectAccountDao.search(sc, searchFilter); + } + + public ProjectInvitation generateInvitation(long projectId, Long accountId) { + //verify if the invitation was already generated + ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId); + + if (invite != null) { + if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) { + throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + projectId); + } else { + if (invite.getState() == ProjectInvitation.State.Pending) { + expireInvitation(invite); + } + } + } + + return _projectInvitationDao.persist(new ProjectInvitationVO(projectId, accountId, _accountMgr.getAccount(accountId).getDomainId(), null, null)); + } + + private boolean expireInvitation(ProjectInvitationVO invite) { + s_logger.debug("Expiring invitation id=" + invite.getId()); + invite.setState(ProjectInvitation.State.Expired); + return _projectInvitationDao.update(invite.getId(), invite); + } + + @Override + public List listProjectInvitations(Long projectId, String accountName, Long domainId, String state, boolean activeOnly, Long startIndex, Long pageSizeVal) { + Account caller = UserContext.current().getCaller(); + Long accountId = null; + String domainPath = null; + + if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { + if (domainId == null) { + domainPath = _domainMgr.getDomain(caller.getDomainId()).getPath(); + } + } else if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL){ + // regular user is constraint to only his account + accountId = caller.getId(); + } + + if (domainId != null) { + Domain domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); + } + _accountMgr.checkAccess(caller, domain, null); + if (accountName != null) { + Account account = _accountDao.findActiveAccount(accountName, domainId); + if (account == null) { + throw new InvalidParameterValueException("Unable to find account by name " + accountName + " in domain " + domainId); + } + + _accountMgr.checkAccess(caller, null, account); + accountId = account.getId(); + } + } + + Filter searchFilter = new Filter(ProjectInvitationVO.class, "id", true, startIndex, pageSizeVal); + + SearchBuilder sb = _projectInvitationDao.createSearchBuilder(); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ); + sb.and("projectId", sb.entity().getProjectId(), SearchCriteria.Op.EQ); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and("created", sb.entity().getCreated(), SearchCriteria.Op.GT); + + if (domainPath != null) { + // do a domain LIKE match for the admin case if isRecursive is true + SearchBuilder domainSearch = _domainDao.createSearchBuilder(); + domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); + sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); + } + + SearchCriteria sc = sb.create(); + + if (domainPath != null) { + sc.setJoinParameters("domainSearch", "path", domainPath); + } + + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + + if (projectId != null){ + sc.setParameters("projectId", projectId); + } + + if (state != null) { + sc.setParameters("state", state); + } + + if (activeOnly) { + sc.setParameters("state", ProjectInvitation.State.Pending); + sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - _invitationTimeOut)); + } + + return _projectInvitationDao.search(sc, searchFilter); + } + + @Override @DB + public boolean joinProject(long projectId, String accountName) { + Account caller = UserContext.current().getCaller(); + Long accountId = null; + boolean result = true; + + //check that the project exists + Project project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + if (accountName != null) { + //check that account-to-remove exists + Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); + if (account == null) { + throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getProjectDomainId()), AccessType.ModifyProject); + accountId = account.getId(); + } else { + accountId = caller.getId(); + } + + //check that invitation exists + ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId); + if (invite != null) { + if (!_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) { + expireInvitation(invite); + throw new InvalidParameterValueException("Invitation is expired for account id=" + accountName + " to the project id=" + projectId); + } else { + Transaction txn = Transaction.currentTxn(); + txn.start(); + //complete invitation + s_logger.debug("Marking invitation " + invite + " with state " + ProjectInvitation.State.Completed); + invite.setState(ProjectInvitation.State.Completed); + result = _projectInvitationDao.update(invite.getId(), invite); + + if (result) { + //check if account already exists for the project (was added before invitation got accepted) + ProjectAccount projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, accountId); + if (projectAccount != null) { + s_logger.debug("Account " + accountName + " already added to the project id=" + projectId); + } else { + assignAccountToProject(project, accountId, ProjectAccount.Role.Regular); + } + } else { + s_logger.warn("Failed to update project invitation " + invite + " with state " + ProjectInvitation.State.Completed); + } + + txn.commit(); + } + } else { + throw new InvalidParameterValueException("Unable to find invitation for account id=" + accountName + " to the project id=" + projectId); + } + + return result; + } } diff --git a/server/src/com/cloud/projects/ProjectVO.java b/server/src/com/cloud/projects/ProjectVO.java index 29f27120454..e305bf0a0a4 100644 --- a/server/src/com/cloud/projects/ProjectVO.java +++ b/server/src/com/cloud/projects/ProjectVO.java @@ -113,7 +113,7 @@ public class ProjectVO implements Project{ @Override public String toString() { StringBuilder buf = new StringBuilder("Project["); - buf.append(id).append("|").append(name).append("|domainid=").append(domainId).append("]"); + buf.append(id).append("|name=").append(name).append("|domainid=").append(domainId).append("]"); return buf.toString(); } diff --git a/server/src/com/cloud/projects/dao/ProjectAccountDao.java b/server/src/com/cloud/projects/dao/ProjectAccountDao.java index 6851880934f..4a010213938 100644 --- a/server/src/com/cloud/projects/dao/ProjectAccountDao.java +++ b/server/src/com/cloud/projects/dao/ProjectAccountDao.java @@ -1,3 +1,20 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ package com.cloud.projects.dao; import java.util.List; @@ -13,4 +30,7 @@ public interface ProjectAccountDao extends GenericDao{ boolean canAccessAccount(long accountId, long projectAccountId); boolean canAccessDomain(long accountId, long projectDomainId); + + boolean canModifyProjectAccount(long accountId, long projectAccountId); + boolean canModifyProjectDomain(long accountId, long projectDomainId); } diff --git a/server/src/com/cloud/projects/dao/ProjectAccountDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectAccountDaoImpl.java index 85c02e6bb24..fbcb0287396 100644 --- a/server/src/com/cloud/projects/dao/ProjectAccountDaoImpl.java +++ b/server/src/com/cloud/projects/dao/ProjectAccountDaoImpl.java @@ -1,3 +1,20 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ package com.cloud.projects.dao; import java.util.List; @@ -15,10 +32,8 @@ import com.cloud.utils.db.SearchCriteria; @Local(value={ProjectAccountDao.class}) public class ProjectAccountDaoImpl extends GenericDaoBase implements ProjectAccountDao { private static final Logger s_logger = Logger.getLogger(ProjectAccountDaoImpl.class); - protected final SearchBuilder AllFieldsSearch; - protected ProjectAccountDaoImpl() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("role", AllFieldsSearch.entity().getAccountRole(), SearchCriteria.Op.EQ); @@ -80,5 +95,32 @@ public class ProjectAccountDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("projectAccountId", projectAccountId); + sc.setParameters("role", ProjectAccount.Role.Owner); + + if (findOneBy(sc) != null) { + return true; + } else { + return false; + } + } + @Override + public boolean canModifyProjectDomain(long accountId, long projectDomainId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("projectDomainId", projectDomainId); + sc.setParameters("role", ProjectAccount.Role.Owner); + + if (findOneBy(sc) != null) { + return true; + } else { + return false; + } + } } diff --git a/server/src/com/cloud/projects/dao/ProjectDao.java b/server/src/com/cloud/projects/dao/ProjectDao.java index 66516f55160..f658f46320a 100644 --- a/server/src/com/cloud/projects/dao/ProjectDao.java +++ b/server/src/com/cloud/projects/dao/ProjectDao.java @@ -1,3 +1,20 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ package com.cloud.projects.dao; import com.cloud.projects.ProjectVO; diff --git a/server/src/com/cloud/projects/dao/ProjectInvitationDao.java b/server/src/com/cloud/projects/dao/ProjectInvitationDao.java new file mode 100644 index 00000000000..21089426da8 --- /dev/null +++ b/server/src/com/cloud/projects/dao/ProjectInvitationDao.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.projects.dao; + +import java.util.List; + +import com.cloud.projects.ProjectInvitationVO; +import com.cloud.utils.db.GenericDao; + +public interface ProjectInvitationDao extends GenericDao{ + ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId); + List listExpiredInvitations(); + boolean expirePendingInvitations(long timeOut); + boolean isActive(long id, long timeout); +} diff --git a/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java new file mode 100644 index 00000000000..8ef568d8db1 --- /dev/null +++ b/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java @@ -0,0 +1,99 @@ +package com.cloud.projects.dao; + +import java.sql.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.projects.ProjectInvitation.State; +import com.cloud.projects.ProjectInvitationVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Local(value={ProjectInvitationDao.class}) +public class ProjectInvitationDaoImpl extends GenericDaoBase implements ProjectInvitationDao { + private static final Logger s_logger = Logger.getLogger(ProjectInvitationDaoImpl.class); + protected final SearchBuilder AllFieldsSearch; + protected final SearchBuilder InactiveSearch; + + protected ProjectInvitationDaoImpl() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("projectId", AllFieldsSearch.entity().getProjectId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("created", AllFieldsSearch.entity().getCreated(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("projectAccountId", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + + InactiveSearch = createSearchBuilder(); + InactiveSearch.and("id", InactiveSearch.entity().getId(), SearchCriteria.Op.EQ); + InactiveSearch.and("accountId", InactiveSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + InactiveSearch.and("projectId", InactiveSearch.entity().getProjectId(), SearchCriteria.Op.EQ); + InactiveSearch.and("created", InactiveSearch.entity().getCreated(), SearchCriteria.Op.LTEQ); + InactiveSearch.and("state", InactiveSearch.entity().getState(), SearchCriteria.Op.EQ); + InactiveSearch.done(); + } + + + @Override + public ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("projectId", projectId); + sc.setParameters("state", State.Pending); + + return findOneBy(sc); + } + + @Override + public List listExpiredInvitations() { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("state", State.Expired); + + return listBy(sc); + } + + @Override + public boolean expirePendingInvitations(long timeout) { + boolean success = true; + + SearchCriteria sc = InactiveSearch.create(); + sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout)); + sc.setParameters("state", State.Pending); + + List invitationsToExpire = listBy(sc); + for (ProjectInvitationVO invitationToExpire : invitationsToExpire) { + invitationToExpire.setState(State.Expired); + if (!update(invitationToExpire.getId(), invitationToExpire)) { + s_logger.warn("Fail to expire invitation " + invitationToExpire.toString()); + success = false; + } + } + + return success; + } + + @Override + public boolean isActive(long id, long timeout) { + SearchCriteria sc = InactiveSearch.create(); + + sc.setParameters("id", id); + + if (findOneBy(sc) == null) { + s_logger.warn("Unable to find project invitation by id " + id); + return false; + } + + sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout)); + + if (findOneBy(sc) == null) { + return true; + } else { + return false; + } + } + +} diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 118677fff75..dab2188f413 100644 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -323,7 +323,7 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager{ return limits; } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountId != null) { //Verify account information and permissions @@ -356,7 +356,7 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager{ _accountMgr.checkAccess(caller, null, _accountDao.findById(vo.getAccountId())); limits.add(vo); } else if (vo.getDomainId() != null) { - _accountMgr.checkAccess(caller, _domainDao.findById(vo.getDomainId())); + _accountMgr.checkAccess(caller, _domainDao.findById(vo.getDomainId()), null); limits.add(vo); } @@ -487,7 +487,7 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager{ ownerId = accountId; } else if (domainId != null) { Domain domain = _entityMgr.findById(Domain.class, domainId); - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if ((caller.getDomainId() == domainId.longValue()) && caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) { // if the admin is trying to update their own domain, disallow... throw new PermissionDeniedException("Unable to update resource limit for domain " + domainId + ", permission denied"); @@ -547,7 +547,7 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager{ if (domain == null) { throw new InvalidParameterValueException("Please specify a valid domain ID."); } - _accountMgr.checkAccess(callerAccount, domain); + _accountMgr.checkAccess(callerAccount, domain, null); if (accountName != null) { Account userAccount = _accountMgr.getActiveAccountByName(accountName, domainId); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 6267b6933d9..8fe5daa880e 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1371,7 +1371,7 @@ public class ManagementServerImpl implements ManagementServer { if (domain == null) { throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountName != null) { Account account = _accountDao.findActiveAccount(accountName, domainId); @@ -1931,7 +1931,7 @@ public class ManagementServerImpl implements ManagementServer { if (domain == null) { throw new InvalidParameterValueException("Unable to find domain by id " + domainId); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (accountName != null) { Account userAccount = _accountDao.findActiveAccount(accountName, domainId); @@ -2288,7 +2288,7 @@ public class ManagementServerImpl implements ManagementServer { // check permissions Account caller = UserContext.current().getCaller(); - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); //domain name is unique in the cloud if (domainName != null) { @@ -2617,7 +2617,7 @@ public class ManagementServerImpl implements ManagementServer { throw new InvalidParameterValueException("Unable to find domain by id " + domainId); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); if (acctName != null) { Account userAccount = _accountDao.findActiveAccount(acctName, domainId); diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index ceec0b89476..a165c44466c 100755 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -42,7 +42,7 @@ public interface AccountManager extends AccountService { boolean deleteAccount(AccountVO account, long callerUserId, Account caller); - void checkAccess(Account account, Domain domain) throws PermissionDeniedException; + void checkAccess(Account account, Domain domain, AccessType accessType) throws PermissionDeniedException; void checkAccess(Account account, AccessType accessType, ControlledEntity... entities) throws PermissionDeniedException; diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 389558ffbfa..600b1084d86 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -259,9 +259,9 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } @Override - public void checkAccess(Account caller, Domain domain) throws PermissionDeniedException { + public void checkAccess(Account caller, Domain domain, AccessType accessType) throws PermissionDeniedException { for (SecurityChecker checker : _securityCheckers) { - if (checker.checkAccess(caller, domain)) { + if (checker.checkAccess(caller, domain, accessType)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Access granted to " + caller + " to " + domain + " by " + checker.getName()); } @@ -316,7 +316,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag throw new PermissionDeniedException("Domain is not found.", caller, domain.getValue()); } try { - checker.checkAccess(caller, d); + checker.checkAccess(caller, d, accessType); } catch (PermissionDeniedException e) { e.addDetails(caller, domain.getValue()); throw e; @@ -611,7 +611,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } //Check permissions - checkAccess(UserContext.current().getCaller(), domain); + checkAccess(UserContext.current().getCaller(), domain, null); if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) { @@ -663,7 +663,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag throw new CloudRuntimeException("The user cannot be created as domain " + domain.getName() + " is being deleted"); } - checkAccess(UserContext.current().getCaller(), domain); + checkAccess(UserContext.current().getCaller(), domain, null); Account account = _accountDao.findActiveAccount(accountName, domainId); if (account == null) { @@ -1177,7 +1177,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag if (owner == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); } - checkAccess(caller, domain); + checkAccess(caller, domain, null); return owner; } else if (!isAdmin(caller.getType()) && accountName != null && domainId != null) { @@ -1243,7 +1243,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag throw new InvalidParameterValueException("Unable to find the domain by id=" + domainId); } - checkAccess(caller, domain); + checkAccess(caller, domain, null); if (accountName != null) { Account owner = getActiveAccountByName(accountName, domainId); diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index 23f99b25d02..a6b718ffadb 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -135,7 +135,7 @@ public class DomainManagerImpl implements DomainManager, DomainService, Manager{ throw new CloudRuntimeException("The domain cannot be created as the parent domain " + parentDomain.getName() + " is being deleted"); } - _accountMgr.checkAccess(caller, parentDomain); + _accountMgr.checkAccess(caller, parentDomain, null); return createDomain(name, parentId, caller.getId(), networkDomain, null); @@ -213,7 +213,7 @@ public class DomainManagerImpl implements DomainManager, DomainService, Manager{ throw new PermissionDeniedException("Can't delete ROOT domain"); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); //mark domain as inactive s_logger.debug("Marking domain id=" + domainId + " as " + Domain.State.Inactive + " before actually deleting it"); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 64d0a9475af..65ab7ee2c25 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2372,9 +2372,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); } // check that caller can operate with domain - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); // check that vm owner can create vm in the domain - _accountMgr.checkAccess(owner, domain); + _accountMgr.checkAccess(owner, domain, null); } // check if account/domain is with in resource limits to create a new vm @@ -2979,7 +2979,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (domain == null) { throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); } - _accountMgr.checkAccess(caller, domain); + _accountMgr.checkAccess(caller, domain, null); } boolean isAdmin = false; @@ -3346,7 +3346,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager // VV 5: check that vm owner can create vm in the domain DomainVO domain = _domainDao.findById(oldAccount.getDomainId()); - _accountMgr.checkAccess(newAccount, domain); + _accountMgr.checkAccess(newAccount, domain, null); DataCenterVO zone = _dcDao.findById(vm.getDataCenterIdToDeployIn()); VMInstanceVO vmoi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index a33f6f20da7..ad4882da019 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -179,7 +179,7 @@ public class MockAccountManagerImpl implements Manager, AccountManager { } @Override - public void checkAccess(Account account, Domain domain) throws PermissionDeniedException { + public void checkAccess(Account account, Domain domain, AccessType accessType) throws PermissionDeniedException { // TODO Auto-generated method stub } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index b368f595e37..c8e7e7dfbf6 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1672,7 +1672,7 @@ CREATE TABLE `cloud`.`projects` ( `project_domain_id` bigint unsigned NOT NULL, `domain_id` bigint unsigned NOT NULL, `created` datetime COMMENT 'date created', - `removed` datetime COMMENT 'date removed',\ + `removed` datetime COMMENT 'date removed', `state` varchar(255) NOT NULL COMMENT 'state of the project (Active/Inactive/Suspended)', PRIMARY KEY (`id`), CONSTRAINT `fk_projects__project_account_id` FOREIGN KEY(`project_account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, @@ -1685,14 +1685,36 @@ CREATE TABLE `cloud`.`projects` ( CREATE TABLE `cloud`.`project_account` ( `id` bigint unsigned NOT NULL auto_increment, `account_id` bigint unsigned NOT NULL COMMENT'account id', - `account_role` varchar(255) COMMENT 'Account role in the project (Owner or Regular)', + `account_role` varchar(255) NOT NULL DEFAULT 'Regular' COMMENT 'Account role in the project (Owner or Regular)', `project_id` bigint unsigned NOT NULL COMMENT 'project id', + `project_account_id` bigint unsigned NOT NULL, + `project_domain_id` bigint unsigned NOT NULL, + `created` datetime COMMENT 'date created', PRIMARY KEY (`id`), CONSTRAINT `fk_project_account__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_project_account__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_account__project_account_id` FOREIGN KEY(`project_account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_account__project_domain_id` FOREIGN KEY(`project_domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, UNIQUE (`account_id`, `project_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`project_invitations` ( + `id` bigint unsigned NOT NULL auto_increment, + `project_id` bigint unsigned NOT NULL COMMENT 'project id', + `account_id` bigint unsigned COMMENT 'account id', + `domain_id` bigint unsigned COMMENT 'domain id', + `email` varchar(255) COMMENT 'email', + `token` varchar(255) COMMENT 'token', + `state` varchar(255) NOT NULL DEFAULT 'Pending' COMMENT 'the state of the invitation', + `created` datetime COMMENT 'date created', + PRIMARY KEY (`id`), + CONSTRAINT `fk_project_invitations__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_invitations__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_invitations__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + CREATE TABLE `cloud`.`elastic_lb_vm_map` ( `id` bigint unsigned NOT NULL auto_increment, `ip_addr_id` bigint unsigned NOT NULL, diff --git a/setup/db/db/schema-2212to30.sql b/setup/db/db/schema-2212to30.sql index 85fe3a2a860..d60f90b1443 100644 --- a/setup/db/db/schema-2212to30.sql +++ b/setup/db/db/schema-2212to30.sql @@ -50,19 +50,43 @@ CREATE TABLE `cloud`.`projects` ( CREATE TABLE `cloud`.`project_account` ( `id` bigint unsigned NOT NULL auto_increment, `account_id` bigint unsigned NOT NULL COMMENT'account id', - `account_role` varchar(255) COMMENT 'Account role in the project (Owner or Regular)', + `account_role` varchar(255) NOT NULL DEFAULT 'Regular' COMMENT 'Account role in the project (Owner or Regular)', `project_id` bigint unsigned NOT NULL COMMENT 'project id', + `project_account_id` bigint unsigned NOT NULL, + `project_domain_id` bigint unsigned NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `fk_project_account__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_project_account__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_account__project_account_id` FOREIGN KEY(`project_account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_account__project_domain_id` FOREIGN KEY(`project_domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, UNIQUE (`account_id`, `project_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`project_invitations` ( + `id` bigint unsigned NOT NULL auto_increment, + `project_id` bigint unsigned NOT NULL COMMENT 'project id', + `account_id` bigint unsigned COMMENT 'account id', + `domain_id` bigint unsigned COMMENT 'domain id', + `email` varchar(255) COMMENT 'email', + `token` varchar(255) COMMENT 'token', + `state` varchar(255) unsigned NOT NULL DEFAULT 'Pending' COMMENT 'the state of the invitation', + `created` datetime COMMENT 'date created', + PRIMARY KEY (`id`), + CONSTRAINT `fk_project_invitations__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_invitations__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_invitations__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + ALTER TABLE domain ADD COLUMN `type` varchar(255) NOT NULL DEFAULT 'Normal' COMMENT 'type of the domain - can be Normal or Project'; + INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'max.project.user.vms', '20', 'The default maximum number of user VMs that can be deployed for a project'); INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'max.project.public.ips', '20', 'The default maximum number of public IPs that can be consumed by a project'); INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'max.project.templates', '20', 'The default maximum number of templates that can be deployed for a project'); INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'max.project.snapshots', '20', 'The default maximum number of snapshots that can be created for a project'); INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'max.project.volumes', '20', 'The default maximum number of volumes that can be created for a project'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.invite.required', 'false', 'If invitation confirmation is required when add account to project. Default value is false'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.invite.timeout', '86400', 'Invitation expiration time (in seconds). Default is 1 day - 86400 seconds'); +