Skip to content

Commit 911f951

Browse files
authored
Handle console session in multiple management servers (#7094)
1 parent e8c32d6 commit 911f951

File tree

8 files changed

+242
-10
lines changed

8 files changed

+242
-10
lines changed

api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ public interface ConsoleAccessManager extends Manager {
2626
boolean isSessionAllowed(String sessionUuid);
2727

2828
void removeSessions(String[] sessionUuids);
29+
30+
void removeSession(String sessionUuid);
2931
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.vm;
21+
22+
import javax.persistence.Column;
23+
import javax.persistence.Entity;
24+
import javax.persistence.GeneratedValue;
25+
import javax.persistence.GenerationType;
26+
import javax.persistence.Id;
27+
import javax.persistence.Table;
28+
import java.util.Date;
29+
30+
@Entity
31+
@Table(name = "console_session")
32+
public class ConsoleSessionVO {
33+
34+
@Id
35+
@GeneratedValue(strategy = GenerationType.IDENTITY)
36+
@Column(name = "id")
37+
private long id;
38+
39+
@Column(name = "uuid")
40+
private String uuid;
41+
42+
@Column(name = "created")
43+
private Date created;
44+
45+
@Column(name = "account_id")
46+
private long accountId;
47+
48+
@Column(name = "user_id")
49+
private long userId;
50+
51+
@Column(name = "instance_id")
52+
private long instanceId;
53+
54+
@Column(name = "host_id")
55+
private long hostId;
56+
57+
@Column(name = "removed")
58+
private Date removed;
59+
60+
public long getId() {
61+
return id;
62+
}
63+
64+
public void setId(long id) {
65+
this.id = id;
66+
}
67+
68+
public String getUuid() {
69+
return uuid;
70+
}
71+
72+
public void setUuid(String uuid) {
73+
this.uuid = uuid;
74+
}
75+
76+
public Date getCreated() {
77+
return created;
78+
}
79+
80+
public void setCreated(Date created) {
81+
this.created = created;
82+
}
83+
84+
public long getAccountId() {
85+
return accountId;
86+
}
87+
88+
public void setAccountId(long accountId) {
89+
this.accountId = accountId;
90+
}
91+
92+
public long getUserId() {
93+
return userId;
94+
}
95+
96+
public void setUserId(long userId) {
97+
this.userId = userId;
98+
}
99+
100+
public long getInstanceId() {
101+
return instanceId;
102+
}
103+
104+
public void setInstanceId(long instanceId) {
105+
this.instanceId = instanceId;
106+
}
107+
108+
public long getHostId() {
109+
return hostId;
110+
}
111+
112+
public void setHostId(long hostId) {
113+
this.hostId = hostId;
114+
}
115+
116+
public Date getRemoved() {
117+
return removed;
118+
}
119+
120+
public void setRemoved(Date removed) {
121+
this.removed = removed;
122+
}
123+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.vm.dao;
21+
22+
import com.cloud.vm.ConsoleSessionVO;
23+
import com.cloud.utils.db.GenericDao;
24+
25+
public interface ConsoleSessionDao extends GenericDao<ConsoleSessionVO, Long> {
26+
27+
void removeSession(String sessionUuid);
28+
29+
boolean isSessionAllowed(String sessionUuid);
30+
31+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package com.cloud.vm.dao;
21+
22+
import com.cloud.vm.ConsoleSessionVO;
23+
import com.cloud.utils.db.GenericDaoBase;
24+
25+
public class ConsoleSessionDaoImpl extends GenericDaoBase<ConsoleSessionVO, Long> implements ConsoleSessionDao {
26+
27+
@Override
28+
public void removeSession(String sessionUuid) {
29+
ConsoleSessionVO session = findByUuid(sessionUuid);
30+
remove(session.getId());
31+
}
32+
33+
@Override
34+
public boolean isSessionAllowed(String sessionUuid) {
35+
return findByUuid(sessionUuid) != null;
36+
}
37+
}

engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<bean id="accountJoinDaoImpl" class="com.cloud.api.query.dao.AccountJoinDaoImpl" />
4646
<bean id="accountVlanMapDaoImpl" class="com.cloud.dc.dao.AccountVlanMapDaoImpl" />
4747
<bean id="alertDaoImpl" class="com.cloud.alert.dao.AlertDaoImpl" />
48+
<bean id="consoleSessionDaoImpl" class="com.cloud.vm.dao.ConsoleSessionDaoImpl" />
4849
<bean id="asyncJobJoinDaoImpl" class="com.cloud.api.query.dao.AsyncJobJoinDaoImpl" />
4950
<bean id="autoScalePolicyConditionMapDaoImpl" class="com.cloud.network.as.dao.AutoScalePolicyConditionMapDaoImpl" />
5051
<bean id="autoScalePolicyDaoImpl" class="com.cloud.network.as.dao.AutoScalePolicyDaoImpl" />

engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,3 +1039,21 @@ WHERE role_id = (SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User -
10391039
INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`)
10401040
SELECT UUID(), `roles`.`id`, 'isAccountAllowedToCreateOfferingsWithTags', 'ALLOW'
10411041
FROM `cloud`.`roles` WHERE `role_type` = 'DomainAdmin';
1042+
1043+
--- Create table for handling console sessions #7094
1044+
1045+
CREATE TABLE IF NOT EXISTS `cloud`.`console_session` (
1046+
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
1047+
`uuid` varchar(40) NOT NULL COMMENT 'UUID generated for the session',
1048+
`created` datetime NOT NULL COMMENT 'When the session was created',
1049+
`account_id` bigint(20) unsigned NOT NULL COMMENT 'Account who generated the session',
1050+
`user_id` bigint(20) unsigned NOT NULL COMMENT 'User who generated the session',
1051+
`instance_id` bigint(20) unsigned NOT NULL COMMENT 'VM for which the session was generated',
1052+
`host_id` bigint(20) unsigned NOT NULL COMMENT 'Host where the VM was when the session was generated',
1053+
`removed` datetime COMMENT 'When the session was removed/used',
1054+
CONSTRAINT `fk_consolesession__account_id` FOREIGN KEY(`account_id`) REFERENCES `cloud`.`account` (`id`),
1055+
CONSTRAINT `fk_consolesession__user_id` FOREIGN KEY(`user_id`) REFERENCES `cloud`.`user`(`id`),
1056+
CONSTRAINT `fk_consolesession__instance_id` FOREIGN KEY(`instance_id`) REFERENCES `cloud`.`vm_instance`(`id`),
1057+
CONSTRAINT `fk_consolesession__host_id` FOREIGN KEY(`host_id`) REFERENCES `cloud`.`host`(`id`),
1058+
CONSTRAINT `uc_consolesession__uuid` UNIQUE (`uuid`)
1059+
);

server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,13 @@ public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticat
106106
}
107107

108108
if (!consoleAccessManager.isSessionAllowed(sessionUuid)) {
109-
s_logger.error("Invalid session, only one session allowed per token");
109+
s_logger.error(String.format("Session [%s] has been already used or does not exist.", sessionUuid));
110110
return new ConsoleAccessAuthenticationAnswer(cmd, false);
111111
}
112112

113+
s_logger.debug(String.format("Removing session [%s] as it was just used.", sessionUuid));
114+
consoleAccessManager.removeSession(sessionUuid);
115+
113116
if (!ticket.equals(ticketInUrl)) {
114117
Date now = new Date();
115118
// considering of minute round-up

server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,20 @@
3838
import com.cloud.utils.component.ManagerBase;
3939
import com.cloud.utils.db.EntityManager;
4040
import com.cloud.utils.exception.CloudRuntimeException;
41+
import com.cloud.vm.ConsoleSessionVO;
4142
import com.cloud.vm.UserVmDetailVO;
4243
import com.cloud.vm.VirtualMachine;
4344
import com.cloud.vm.VirtualMachineManager;
4445
import com.cloud.vm.VmDetailConstants;
46+
import com.cloud.vm.dao.ConsoleSessionDao;
4547
import com.cloud.vm.dao.UserVmDetailsDao;
4648
import com.google.gson.Gson;
4749
import com.google.gson.GsonBuilder;
4850
import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint;
4951
import org.apache.cloudstack.context.CallContext;
5052
import org.apache.cloudstack.framework.security.keys.KeysManager;
5153
import org.apache.commons.codec.binary.Base64;
54+
import org.apache.commons.lang3.ArrayUtils;
5255
import org.apache.commons.lang3.ObjectUtils;
5356
import org.apache.commons.lang3.StringUtils;
5457
import org.apache.log4j.Logger;
@@ -60,10 +63,8 @@
6063

6164
import java.util.Arrays;
6265
import java.util.Date;
63-
import java.util.HashSet;
6466
import java.util.List;
6567
import java.util.Map;
66-
import java.util.Set;
6768
import java.util.UUID;
6869

6970
public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAccessManager {
@@ -84,6 +85,8 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
8485
private AgentManager agentManager;
8586
@Inject
8687
private ConsoleProxyManager consoleProxyManager;
88+
@Inject
89+
private ConsoleSessionDao consoleSessionDao;
8790

8891
private static KeysManager secretKeysManager;
8992
private final Gson gson = new GsonBuilder().create();
@@ -94,12 +97,9 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
9497
VirtualMachine.State.Stopped, VirtualMachine.State.Error, VirtualMachine.State.Destroyed
9598
);
9699

97-
private static Set<String> allowedSessions;
98-
99100
@Override
100101
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
101102
ConsoleAccessManagerImpl.secretKeysManager = keysManager;
102-
ConsoleAccessManagerImpl.allowedSessions = new HashSet<>();
103103
return super.configure(name, params);
104104
}
105105

@@ -146,16 +146,23 @@ public ConsoleEndpoint generateConsoleEndpoint(Long vmId, String extraSecurityTo
146146

147147
@Override
148148
public boolean isSessionAllowed(String sessionUuid) {
149-
return allowedSessions.contains(sessionUuid);
149+
return consoleSessionDao.isSessionAllowed(sessionUuid);
150150
}
151151

152152
@Override
153153
public void removeSessions(String[] sessionUuids) {
154-
for (String r : sessionUuids) {
155-
allowedSessions.remove(r);
154+
if (ArrayUtils.isNotEmpty(sessionUuids)) {
155+
for (String sessionUuid : sessionUuids) {
156+
removeSession(sessionUuid);
157+
}
156158
}
157159
}
158160

161+
@Override
162+
public void removeSession(String sessionUuid) {
163+
consoleSessionDao.removeSession(sessionUuid);
164+
}
165+
159166
protected boolean checkSessionPermission(VirtualMachine vm, Account account) {
160167
if (accountManager.isRootAdmin(account.getId())) {
161168
return true;
@@ -289,7 +296,7 @@ private ConsoleEndpoint composeConsoleAccessEndpoint(String rootUrl, VirtualMach
289296
String url = generateConsoleAccessUrl(rootUrl, param, token, vncPort, vm);
290297

291298
s_logger.debug("Adding allowed session: " + sessionUuid);
292-
allowedSessions.add(sessionUuid);
299+
persistConsoleSession(sessionUuid, vm.getId(), hostVo.getId());
293300
managementServer.setConsoleAccessForVm(vm.getId(), sessionUuid);
294301

295302
ConsoleEndpoint consoleEndpoint = new ConsoleEndpoint(true, url);
@@ -303,6 +310,16 @@ private ConsoleEndpoint composeConsoleAccessEndpoint(String rootUrl, VirtualMach
303310
return consoleEndpoint;
304311
}
305312

313+
protected void persistConsoleSession(String sessionUuid, long instanceId, long hostId) {
314+
ConsoleSessionVO consoleSessionVo = new ConsoleSessionVO();
315+
consoleSessionVo.setUuid(sessionUuid);
316+
consoleSessionVo.setAccountId(CallContext.current().getCallingAccountId());
317+
consoleSessionVo.setUserId(CallContext.current().getCallingUserId());
318+
consoleSessionVo.setInstanceId(instanceId);
319+
consoleSessionVo.setHostId(hostId);
320+
consoleSessionDao.persist(consoleSessionVo);
321+
}
322+
306323
private String generateConsoleAccessUrl(String rootUrl, ConsoleProxyClientParam param, String token, int vncPort,
307324
VirtualMachine vm) {
308325
StringBuilder sb = new StringBuilder(rootUrl);

0 commit comments

Comments
 (0)