Skip to content

Commit

Permalink
[PDI-12814] - Need to prohibit empty names when adding new users or r…
Browse files Browse the repository at this point in the history
…oles

- introduce RepositorySecurityUserValidator and RepositorySecurityRoleValidator interfaces for doing validation more flexible
- add tests
  • Loading branch information
Andrey Khayrutdinov committed Oct 7, 2015
1 parent 16011b7 commit af1ae19
Show file tree
Hide file tree
Showing 8 changed files with 462 additions and 18 deletions.
@@ -0,0 +1,58 @@
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

package org.pentaho.di.repository;


import static org.apache.commons.lang.StringUtils.isBlank;

/**
* @author Andrey Khayrutdinov
*/
public class RepositoryCommonValidations {

/**
* Validates {@code user}'s data. Common rule for all repositories is: both login and name must contain at least one
* meaningful char.
*
* @param user user
* @return {@code true} if user's login and name are not empty
* @throws NullPointerException is {@code user} is {@code null}
*/
public static boolean checkUserInfo( IUser user ) {
return !isBlank( user.getLogin() ) && !isBlank( user.getName() );
}

/**
* Normalizes {@code user}'s data. According to {@linkplain #checkUserInfo(IUser) common rules}, simply trims login
* and name.
*
* @param user user
* @return normalized instance
* @throws NullPointerException if {@code user} is {@code null} or {@code user}'s login and name
*/
public static IUser normalizeUserInfo( IUser user ) {
user.setLogin( user.getLogin().trim() );
user.setName( user.getName().trim() );
return user;
}
}
@@ -0,0 +1,46 @@
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

package org.pentaho.di.repository;

/**
* @author Andrey Khayrutdinov
*/
public interface RepositorySecurityUserValidator extends RepositorySecurityManager {

/**
* Validates {@code user} and returns {@code true} if all its data is valid.<br/>
* Note: this method performs fail-fast approach and does not return any details.
*
* @param user user's info
* @return {@code true} or {@code false} depending on whether or not user's info is valid
*/
boolean validateUserInfo( IUser user );

/**
* Performs normalization over {@code user} due to validation rules.</br>
* Note: normalized is not guaranteed to pass validation rules
*
* @param user user's info
*/
void normalizeUserInfo(IUser user);
}
Expand Up @@ -31,16 +31,18 @@
import org.pentaho.di.repository.IUser;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.RepositoryCapabilities;
import org.pentaho.di.repository.RepositoryCommonValidations;
import org.pentaho.di.repository.RepositoryMeta;
import org.pentaho.di.repository.RepositoryOperation;
import org.pentaho.di.repository.RepositorySecurityManager;
import org.pentaho.di.repository.RepositorySecurityProvider;
import org.pentaho.di.repository.RepositorySecurityUserValidator;
import org.pentaho.di.repository.UserInfo;
import org.pentaho.di.repository.kdr.delegates.KettleDatabaseRepositoryConnectionDelegate;
import org.pentaho.di.repository.kdr.delegates.KettleDatabaseRepositoryUserDelegate;

public class KettleDatabaseRepositorySecurityProvider extends BaseRepositorySecurityProvider implements
RepositorySecurityProvider, RepositorySecurityManager {
RepositorySecurityProvider, RepositorySecurityManager, RepositorySecurityUserValidator {

private RepositoryCapabilities capabilities;

Expand All @@ -55,7 +57,7 @@ public class KettleDatabaseRepositorySecurityProvider extends BaseRepositorySecu
* @param userInfo
*/
public KettleDatabaseRepositorySecurityProvider( KettleDatabaseRepository repository,
RepositoryMeta repositoryMeta, IUser userInfo ) {
RepositoryMeta repositoryMeta, IUser userInfo ) {
super( repositoryMeta, userInfo );
this.repository = repository;
this.capabilities = repositoryMeta.getRepositoryCapabilities();
Expand Down Expand Up @@ -90,6 +92,10 @@ public IUser loadUserInfo( String login ) throws KettleException {
}

public void saveUserInfo( IUser userInfo ) throws KettleException {
normalizeUserInfo( userInfo );
if ( !validateUserInfo( userInfo ) ) {
throw new KettleException( "Empty name is not allowed" );
}
userDelegate.saveUserInfo( userInfo );
}

Expand Down Expand Up @@ -171,4 +177,15 @@ public boolean isManaged() throws KettleException {
public boolean isVersioningEnabled( String fullPath ) {
return false;
}


@Override
public boolean validateUserInfo( IUser user ) {
return RepositoryCommonValidations.checkUserInfo( user );
}

@Override
public void normalizeUserInfo( IUser user ) {
RepositoryCommonValidations.normalizeUserInfo( user );
}
}
@@ -0,0 +1,87 @@
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

package org.pentaho.di.repository;

import org.junit.Test;

import static org.junit.Assert.*;
import static org.pentaho.di.repository.RepositoryCommonValidations.checkUserInfo;
import static org.pentaho.di.repository.RepositoryCommonValidations.normalizeUserInfo;

/**
* @author Andrey Khayrutdinov
*/
public class RepositoryCommonValidationsTest {

@Test( expected = NullPointerException.class )
public void checkUserInfo_Null() {
checkUserInfo( null );
}


@Test
public void checkUserInfo_LoginIsNull() {
assertFalse( checkUserInfo( user( null, "name" ) ) );
}

@Test
public void checkUserInfo_LoginIsBlank() {
assertFalse( checkUserInfo( user( "", "name" ) ) );
}

@Test
public void checkUserInfo_LoginContainsSpaces() {
assertFalse( checkUserInfo( user( " \t\n ", "name" ) ) );
}


@Test
public void checkUserInfo_BothAreMeaningful() {
assertTrue( checkUserInfo( user( "login", "name" ) ) );
}


@Test( expected = NullPointerException.class )
public void normalizeUserInfo_Null() {
normalizeUserInfo( null );
}

@Test
public void normalizeUserInfo_Valid() {
IUser normalized = normalizeUserInfo( user( "login", "name" ) );
assertEquals( "login", normalized.getLogin() );
assertEquals( "login", normalized.getName() );
}

@Test
public void normalizeUserInfo_WithSpaces() {
IUser normalized = normalizeUserInfo( user( " login \t\n ", "name" ) );
assertEquals( "login", normalized.getLogin() );
assertEquals( "login", normalized.getName() );
}


private static IUser user( String login, String name ) {
return new UserInfo( login, null, name, name, true );
}
}
@@ -0,0 +1,77 @@
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

package org.pentaho.di.repository.kdr;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.pentaho.di.core.KettleEnvironment;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.repository.UserInfo;
import org.pentaho.di.repository.kdr.delegates.KettleDatabaseRepositoryUserDelegate;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

/**
* @author Andrey Khayrutdinov
*/
public class KettleDatabaseRepositorySecurityProviderTest {

@BeforeClass
public static void initKettle() throws Exception {
KettleEnvironment.init();
}


private KettleDatabaseRepository repository;
private KettleDatabaseRepositorySecurityProvider provider;

@Before
public void setUp() {
repository = new KettleDatabaseRepository();
repository.userDelegate = mock( KettleDatabaseRepositoryUserDelegate.class );
provider =
new KettleDatabaseRepositorySecurityProvider( repository, new KettleDatabaseRepositoryMeta(), new UserInfo() );
}

@Test
public void saveUserInfo_NormalizesInfo_PassesIfNoViolations() throws Exception {
UserInfo info = new UserInfo( "login " );

ArgumentCaptor<UserInfo> captor = ArgumentCaptor.forClass( UserInfo.class );
provider.saveUserInfo( info );
verify( repository.userDelegate ).saveUserInfo( captor.capture() );

info = captor.getValue();
assertEquals( "Spaces should be trimmed", "login", info.getLogin() );
}

@Test( expected = KettleException.class )
public void saveUserInfo_NormalizesInfo_FailsIfStillBreaches() throws Exception {
UserInfo info = new UserInfo( " " );
provider.saveUserInfo( info );
}
}

0 comments on commit af1ae19

Please sign in to comment.