mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-8191: SAML users should have their own accounts
(cherry picked from commit 876c78fe1ba6abe132131b3449b21fd09f2c14e1) Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
2f6691c6b9
commit
552f2ae60c
@ -758,6 +758,7 @@ label.local.storage=Local Storage
|
||||
label.local=Local
|
||||
label.login=Login
|
||||
label.logout=Logout
|
||||
label.saml.login=SAML Login
|
||||
label.LUN.number=LUN \#
|
||||
label.lun=LUN
|
||||
label.make.project.owner=Make account project owner
|
||||
|
||||
@ -572,6 +572,7 @@ label.local=Lokal
|
||||
label.local.storage=Lokaler Speicher
|
||||
label.login=Login
|
||||
label.logout=Abmelden
|
||||
label.saml.login=SAML Login
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.management=Verwaltung
|
||||
|
||||
@ -555,6 +555,7 @@ label.local=local
|
||||
label.local.storage=Almacenamiento Local
|
||||
label.login=Login
|
||||
label.logout=Cerrar sesi\u00c3\u00b3n
|
||||
label.saml.login=SAML Login
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.manage=Administrar
|
||||
|
||||
@ -878,6 +878,7 @@ label.local.storage.enabled=Stockage local activ\u00e9
|
||||
label.local.storage=Stockage local
|
||||
label.login=Connexion
|
||||
label.logout=D\u00e9connexion
|
||||
label.saml.login=SAML Connexion
|
||||
label.lun=LUN
|
||||
label.LUN.number=N\u00b0 LUN
|
||||
label.lxc.traffic.label=Libell\u00e9 trafic LXC
|
||||
|
||||
@ -755,6 +755,7 @@ label.local.storage=\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8
|
||||
label.local=\u30ed\u30fc\u30ab\u30eb
|
||||
label.login=\u30ed\u30b0\u30aa\u30f3
|
||||
label.logout=\u30ed\u30b0\u30aa\u30d5
|
||||
label.saml.login=SAML \u30ed\u30b0\u30aa\u30f3
|
||||
label.LUN.number=LUN \u756a\u53f7
|
||||
label.lun=LUN
|
||||
label.make.project.owner=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u6240\u6709\u8005\u3078\u306e\u5909\u66f4
|
||||
@ -2085,4 +2086,4 @@ label.timezone.colon=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\:
|
||||
label.keep.colon=\u4fdd\u6301\u6570\:
|
||||
label.every=\u6bce\u9031
|
||||
label.day=\u6bce\u6708
|
||||
label.of.month=\u65e5
|
||||
label.of.month=\u65e5
|
||||
|
||||
@ -651,6 +651,7 @@ label.local.storage=\ub85c\uceec \uc2a4\ud1a0\ub9ac\uc9c0
|
||||
label.local=\ub85c\uceec
|
||||
label.login=\ub85c\uadf8\uc778
|
||||
label.logout=\ub85c\uadf8\uc544\uc6c3
|
||||
label.saml.login=SAML \ub85c\uadf8\uc778
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \ubc88\ud638
|
||||
label.make.project.owner=\uacc4\uc815 \uc815\ubcf4 \ud504\ub85c\uc81d\ud2b8 \uc18c\uc720\uc790
|
||||
|
||||
@ -826,6 +826,7 @@ label.local.storage.enabled=Lokale opslag ingeschakeld
|
||||
label.local.storage=Lokale Opslag
|
||||
label.login=Login
|
||||
label.logout=Log uit
|
||||
label.saml.login=SAML Login
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.lxc.traffic.label=LXC verkeerslabel
|
||||
|
||||
@ -292,6 +292,7 @@ label.local.storage.enabled=Pami\u0119\u0107 lokalna w\u0142\u0105czona
|
||||
label.local.storage=Pami\u0119\u0107 lokalna
|
||||
label.login=Zaloguj
|
||||
label.logout=Wyloguj
|
||||
label.saml.login=SAML Zaloguj
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.max.guest.limit=Maksymalna liczba go\u015bci
|
||||
|
||||
@ -728,6 +728,7 @@ label.local.storage.enabled=Storage local habilitada
|
||||
label.local.storage=Storage Local
|
||||
label.login=Entrar
|
||||
label.logout=Sair
|
||||
label.saml.login=SAML Entrar
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.make.project.owner=Criar propriet\u00e1rio de conta de projeto
|
||||
|
||||
@ -689,6 +689,7 @@ label.local.storage=\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u044
|
||||
label.local=\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439
|
||||
label.login=\u0412\u0445\u043e\u0434
|
||||
label.logout=\u0412\u044b\u0445\u043e\u0434
|
||||
label.saml.login=SAML \u0412\u0445\u043e\u0434
|
||||
label.lun=LUN
|
||||
label.LUN.number=LUN \#
|
||||
label.make.project.owner=\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0432\u043b\u0430\u0434\u0435\u043b\u044c\u0446\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430
|
||||
|
||||
@ -755,6 +755,7 @@ label.local.storage=\u672c\u5730\u5b58\u50a8
|
||||
label.local=\u672c\u5730
|
||||
label.login=\u767b\u5f55
|
||||
label.logout=\u6ce8\u9500
|
||||
label.saml.login=SAML \u767b\u5f55
|
||||
label.LUN.number=LUN \u53f7
|
||||
label.lun=LUN
|
||||
label.make.project.owner=\u8bbe\u4e3a\u5e10\u6237\u9879\u76ee\u6240\u6709\u8005
|
||||
@ -2085,4 +2086,4 @@ label.timezone.colon=\u65f6\u533a\:
|
||||
label.keep.colon=\u4fdd\u7559\u6570\u91cf\:
|
||||
label.every=\u6bcf
|
||||
label.day=\u6bcf\u6708
|
||||
label.of.month=\u65e5
|
||||
label.of.month=\u65e5
|
||||
|
||||
@ -23,7 +23,8 @@ import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.CloudAuthenticationException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.DomainManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.dao.UserAccountDao;
|
||||
import com.cloud.utils.HttpUtils;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
@ -73,6 +74,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {})
|
||||
public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator {
|
||||
@ -93,6 +95,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
ConfigurationDao _configDao;
|
||||
@Inject
|
||||
DomainManager _domainMgr;
|
||||
@Inject
|
||||
private UserAccountDao _userAccountDao;
|
||||
|
||||
SAML2AuthManager _samlAuthManager;
|
||||
|
||||
@ -201,11 +205,9 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
}
|
||||
}
|
||||
|
||||
String uniqueUserId = null;
|
||||
String accountName = _configDao.getValue(Config.SAMLUserAccountName.key());
|
||||
String domainString = _configDao.getValue(Config.SAMLUserDomain.key());
|
||||
|
||||
Long domainId = -1L;
|
||||
Long domainId = null;
|
||||
Domain domain = _domainMgr.getDomain(domainString);
|
||||
if (domain != null) {
|
||||
domainId = domain.getId();
|
||||
@ -215,16 +217,17 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
}
|
||||
if (domainId == -1L) {
|
||||
s_logger.error("The default domain ID for SAML users is not set correct, it should be a UUID");
|
||||
if (domainId == null) {
|
||||
s_logger.error("The default domain ID for SAML users is not set correct, it should be a UUID. ROOT domain will be used.");
|
||||
}
|
||||
|
||||
String username = null;
|
||||
String password = SAMLUtils.generateSecureRandomId(); // Random password
|
||||
String firstName = "";
|
||||
String lastName = "";
|
||||
String timeZone = "";
|
||||
String timeZone = "GMT";
|
||||
String email = "";
|
||||
short accountType = 0; // User account
|
||||
|
||||
Assertion assertion = processedSAMLResponse.getAssertions().get(0);
|
||||
NameID nameId = assertion.getSubject().getNameID();
|
||||
@ -234,7 +237,6 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
|
||||
if (nameId.getFormat().equals(NameIDType.PERSISTENT) || nameId.getFormat().equals(NameIDType.EMAIL)) {
|
||||
username = nameId.getValue();
|
||||
uniqueUserId = SAMLUtils.createSAMLId(username);
|
||||
if (nameId.getFormat().equals(NameIDType.EMAIL)) {
|
||||
email = username;
|
||||
}
|
||||
@ -250,9 +252,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
for (Attribute attribute: attributeStatement.getAttributes()) {
|
||||
String attributeName = attribute.getName();
|
||||
String attributeValue = attribute.getAttributeValues().get(0).getDOM().getTextContent();
|
||||
if (attributeName.equalsIgnoreCase("uid") && uniqueUserId == null) {
|
||||
if (attributeName.equalsIgnoreCase("uid") && username == null) {
|
||||
username = attributeValue;
|
||||
uniqueUserId = SAMLUtils.createSAMLId(username);
|
||||
} else if (attributeName.equalsIgnoreCase("givenName")) {
|
||||
firstName = attributeValue;
|
||||
} else if (attributeName.equalsIgnoreCase(("sn"))) {
|
||||
@ -264,17 +265,22 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
||||
}
|
||||
}
|
||||
|
||||
User user = _entityMgr.findByUuid(User.class, uniqueUserId);
|
||||
if (user == null && uniqueUserId != null && username != null
|
||||
&& accountName != null && domainId != null) {
|
||||
CallContext.current().setEventDetails("UserName: " + username + ", FirstName :" + password + ", LastName: " + lastName);
|
||||
user = _accountService.createUser(username, password, firstName, lastName, email, timeZone, accountName, domainId, uniqueUserId);
|
||||
if (username == null && email != null) {
|
||||
username = email;
|
||||
}
|
||||
final String uniqueUserId = SAMLUtils.createSAMLId(username);
|
||||
|
||||
UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
|
||||
if (userAccount == null && uniqueUserId != null && username != null) {
|
||||
CallContext.current().setEventDetails("SAML Account/User with UserName: " + username + ", FirstName :" + password + ", LastName: " + lastName);
|
||||
_accountService.createUserAccount(username, password, firstName, lastName, email, timeZone,
|
||||
username, (short) accountType, domainId, null, null, UUID.randomUUID().toString(), uniqueUserId);
|
||||
}
|
||||
|
||||
if (user != null) {
|
||||
if (userAccount != null) {
|
||||
try {
|
||||
if (_apiServer.verifyUser(user.getId())) {
|
||||
LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, username, user.getPassword(), domainId, null, remoteAddress, params);
|
||||
if (_apiServer.verifyUser(userAccount.getId())) {
|
||||
LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, username, userAccount.getPassword(), domainId, null, remoteAddress, params);
|
||||
resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
|
||||
|
||||
@ -22,10 +22,9 @@ package org.apache.cloudstack.api.command;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.user.DomainManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.user.UserAccountVO;
|
||||
import com.cloud.user.dao.UserAccountDao;
|
||||
import com.cloud.utils.HttpUtils;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import org.apache.cloudstack.api.ApiServerService;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
@ -79,15 +78,15 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
||||
@Mock
|
||||
ConfigurationDao configDao;
|
||||
|
||||
@Mock
|
||||
EntityManager entityMgr;
|
||||
|
||||
@Mock
|
||||
DomainManager domainMgr;
|
||||
|
||||
@Mock
|
||||
AccountService accountService;
|
||||
|
||||
@Mock
|
||||
UserAccountDao userAccountDao;
|
||||
|
||||
@Mock
|
||||
Domain domain;
|
||||
|
||||
@ -139,10 +138,6 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
||||
accountServiceField.setAccessible(true);
|
||||
accountServiceField.set(cmd, accountService);
|
||||
|
||||
Field entityMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_entityMgr");
|
||||
entityMgrField.setAccessible(true);
|
||||
entityMgrField.set(cmd, entityMgr);
|
||||
|
||||
Field domainMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_domainMgr");
|
||||
domainMgrField.setAccessible(true);
|
||||
domainMgrField.set(cmd, domainMgr);
|
||||
@ -151,6 +146,10 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
||||
configDaoField.setAccessible(true);
|
||||
configDaoField.set(cmd, configDao);
|
||||
|
||||
Field userAccountDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_userAccountDao");
|
||||
userAccountDaoField.setAccessible(true);
|
||||
userAccountDaoField.set(cmd, userAccountDao);
|
||||
|
||||
String spId = "someSPID";
|
||||
String url = "someUrl";
|
||||
X509Certificate cert = SAMLUtils.generateRandomX509Certificate(SAMLUtils.generateRandomKeyPair());
|
||||
@ -164,9 +163,10 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
||||
|
||||
Mockito.when(domain.getId()).thenReturn(1L);
|
||||
Mockito.when(domainMgr.getDomain(Mockito.anyString())).thenReturn(domain);
|
||||
UserVO user = new UserVO();
|
||||
user.setUuid(SAMLUtils.createSAMLId("someUID"));
|
||||
Mockito.when(entityMgr.findByUuid(Mockito.eq(User.class), Mockito.anyString())).thenReturn((User) user);
|
||||
UserAccountVO user = new UserAccountVO();
|
||||
user.setUsername(SAMLUtils.createSAMLId("someUID"));
|
||||
user.setId(1000L);
|
||||
Mockito.when(userAccountDao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(user);
|
||||
Mockito.when(apiServer.verifyUser(Mockito.anyLong())).thenReturn(false);
|
||||
|
||||
Map<String, Object[]> params = new HashMap<String, Object[]>();
|
||||
@ -184,7 +184,7 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
||||
}
|
||||
Mockito.verify(configDao, Mockito.atLeastOnce()).getValue(Mockito.anyString());
|
||||
Mockito.verify(domainMgr, Mockito.times(1)).getDomain(Mockito.anyString());
|
||||
Mockito.verify(entityMgr, Mockito.times(1)).findByUuid(Mockito.eq(User.class), Mockito.anyString());
|
||||
Mockito.verify(userAccountDao, Mockito.times(1)).getUserAccount(Mockito.anyString(), Mockito.anyLong());
|
||||
Mockito.verify(apiServer, Mockito.times(1)).verifyUser(Mockito.anyLong());
|
||||
}
|
||||
|
||||
|
||||
@ -1379,14 +1379,6 @@ public enum Config {
|
||||
"false",
|
||||
"Set it to true to enable SAML SSO plugin",
|
||||
null),
|
||||
SAMLUserAccountName(
|
||||
"Advanced",
|
||||
ManagementServer.class,
|
||||
String.class,
|
||||
"saml2.default.accountname",
|
||||
"admin",
|
||||
"The name of the default account to use when creating users from SAML SSO",
|
||||
null),
|
||||
SAMLUserDomain(
|
||||
"Advanced",
|
||||
ManagementServer.class,
|
||||
|
||||
@ -1017,7 +1017,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
throw new InvalidParameterValueException("The user " + userName + " already exists in domain " + domainId);
|
||||
}
|
||||
|
||||
if (networkDomain != null) {
|
||||
if (networkDomain != null && networkDomain.length() > 0) {
|
||||
if (!NetUtils.verifyDomainName(networkDomain)) {
|
||||
throw new InvalidParameterValueException(
|
||||
"Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
|
||||
|
||||
@ -440,7 +440,7 @@ body.login {
|
||||
background: transparent url(../images/sprites.png) -563px -747px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
margin: 7px 238px 0 -1px;
|
||||
margin: 7px 120px 0 -1px;
|
||||
text-align: center;
|
||||
width: 69px;
|
||||
height: 25px;
|
||||
@ -456,6 +456,27 @@ body.login {
|
||||
text-shadow: 0px 1px 2px #000000;
|
||||
}
|
||||
|
||||
.login .fields input[type=samlsubmit] {
|
||||
background: transparent url(../images/sprites.png) -563px -747px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
margin: 7px 120px 0 -1px;
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
height: 15px;
|
||||
display: block;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
float: left;
|
||||
text-indent: -1px;
|
||||
/*+text-shadow:0px 1px 2px #000000;*/
|
||||
-moz-text-shadow: 0px 1px 2px #000000;
|
||||
-webkit-text-shadow: 0px 1px 2px #000000;
|
||||
-o-text-shadow: 0px 1px 2px #000000;
|
||||
text-shadow: 0px 1px 2px #000000;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.login .fields input[type=submit]:hover {
|
||||
background-position: -563px -772px;
|
||||
}
|
||||
@ -13035,4 +13056,3 @@ div.gpugroups div.list-view {
|
||||
background: transparent url("../images/icons.png") no-repeat -626px -209px;
|
||||
padding: 0 0 3px 18px;
|
||||
}
|
||||
|
||||
|
||||
@ -762,6 +762,7 @@ dictionary = {
|
||||
'label.local.storage': '<fmt:message key="label.local.storage" />',
|
||||
'label.login': '<fmt:message key="label.login" />',
|
||||
'label.logout': '<fmt:message key="label.logout" />',
|
||||
'label.saml.login': '<fmt:message key="label.saml.login" />',
|
||||
'label.lun': '<fmt:message key="label.lun" />',
|
||||
'label.LUN.number': '<fmt:message key="label.LUN.number" />',
|
||||
'label.make.project.owner': '<fmt:message key="label.make.project.owner" />',
|
||||
|
||||
@ -67,6 +67,7 @@
|
||||
</div>
|
||||
<!-- Submit (login) -->
|
||||
<input type="submit" value="<fmt:message key="label.login"/>" />
|
||||
<input type="samlsubmit" value="<fmt:message key="label.saml.login"/>" />
|
||||
<!-- Select language -->
|
||||
<div class="select-language">
|
||||
<select name="language">
|
||||
|
||||
@ -346,6 +346,16 @@
|
||||
});
|
||||
},
|
||||
|
||||
samlLoginAction: function(args) {
|
||||
$.cookie('sessionKey', null);
|
||||
$.cookie('username', null);
|
||||
$.cookie('account', null);
|
||||
$.cookie('domainid', null);
|
||||
$.cookie('role', null);
|
||||
$.cookie('timezone', null);
|
||||
window.location.href = createURL('samlSso');
|
||||
},
|
||||
|
||||
// Show cloudStack main UI widget
|
||||
complete: function(args) {
|
||||
var context = {
|
||||
|
||||
@ -120,6 +120,12 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
// SAML Login action
|
||||
$login.find('input[type=samlsubmit]').click(function() {
|
||||
args.samlLoginAction({
|
||||
});
|
||||
});
|
||||
|
||||
// Select language
|
||||
var $languageSelect = $login.find('select[name=language]');
|
||||
$languageSelect.change(function() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user