Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
V
variety
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fact-gitdep
variety
Commits
a1a10a63
Commit
a1a10a63
authored
Dec 20, 2014
by
James Cropcho
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #61 from todvora/master
Variety works with restricted user permissions + tests
parents
558352d0
6a366b35
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
301 additions
and
46 deletions
+301
-46
pom.xml
test/pom.xml
+1
-1
Credentials.java
test/src/main/java/com/github/variety/Credentials.java
+62
-0
MongoShell.java
test/src/main/java/com/github/variety/MongoShell.java
+78
-0
Variety.java
test/src/main/java/com/github/variety/Variety.java
+21
-39
LimitedAccessTest.java
.../test/java/com/github/variety/test/LimitedAccessTest.java
+131
-0
variety.js
variety.js
+8
-6
No files found.
test/pom.xml
View file @
a1a10a63
...
...
@@ -16,7 +16,7 @@
<dependency>
<groupId>
org.mongodb
</groupId>
<artifactId>
mongo-java-driver
</artifactId>
<version>
2.12.
3
</version>
<version>
2.12.
4
</version>
</dependency>
<dependency>
...
...
test/src/main/java/com/github/variety/Credentials.java
0 → 100644
View file @
a1a10a63
package
com
.
github
.
variety
;
import
com.mongodb.BasicDBObjectBuilder
;
import
com.mongodb.DBObject
;
import
com.mongodb.MongoCredential
;
import
com.mongodb.util.JSON
;
/**
* MongoDB access credentials for admin/root with unrestricted access and for user with read only database test
*/
public
enum
Credentials
{
ADMIN
(
"admin"
,
"variety_test_admin"
,
"admin"
,
"['userAdminAnyDatabase', 'readWriteAnyDatabase', 'dbAdminAnyDatabase']"
),
USER
(
"test"
,
"variety_test_user"
,
"test"
,
"['read']"
);
/**
* Name of database, where auth objects are stored.
*/
public
static
final
String
AUTH_DATABASE_NAME
=
"admin"
;
private
final
String
authDatabase
;
private
final
String
username
;
private
final
String
password
;
private
final
String
rolesJson
;
Credentials
(
final
String
authDatabase
,
final
String
username
,
final
String
password
,
final
String
rolesJson
)
{
this
.
authDatabase
=
authDatabase
;
this
.
username
=
username
;
this
.
password
=
password
;
this
.
rolesJson
=
rolesJson
;
}
public
String
getAuthDatabase
()
{
return
authDatabase
;
}
public
String
getUsername
()
{
return
username
;
}
public
String
getPassword
()
{
return
password
;
}
/**
* @return Auth credentials for MongoDB Java driver.
*/
public
MongoCredential
getMongoCredential
()
{
return
MongoCredential
.
createMongoCRCredential
(
getUsername
(),
getAuthDatabase
(),
getPassword
().
toCharArray
());
}
/**
* Convert username, password and roles to MongoDB document for user creation
* @return json document to be passed to createUser (mongodb version >=2.6.x) / addUser function(mongodb 2.4.x)
*/
public
DBObject
getUserDocument
()
{
return
new
BasicDBObjectBuilder
()
.
add
(
"user"
,
username
)
.
add
(
"pwd"
,
password
)
.
add
(
"roles"
,
JSON
.
parse
(
this
.
rolesJson
))
.
get
();
}
}
test/src/main/java/com/github/variety/MongoShell.java
0 → 100644
View file @
a1a10a63
package
com
.
github
.
variety
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.nio.charset.StandardCharsets
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.StringJoiner
;
public
class
MongoShell
{
private
final
boolean
quiet
;
private
final
Credentials
credentials
;
private
final
String
eval
;
private
final
String
database
;
private
final
String
script
;
public
MongoShell
(
final
String
database
,
final
Credentials
credentials
,
final
String
eval
,
final
String
script
,
final
boolean
quiet
)
{
this
.
quiet
=
quiet
;
this
.
credentials
=
credentials
;
this
.
eval
=
eval
;
this
.
database
=
database
;
this
.
script
=
script
;
}
public
String
execute
()
throws
IOException
,
InterruptedException
{
final
List
<
String
>
commands
=
new
ArrayList
<>();
commands
.
add
(
"mongo"
);
if
(
database
!=
null
&&
!
database
.
isEmpty
())
{
commands
.
add
(
this
.
database
);
}
if
(
quiet
)
{
commands
.
add
(
"--quiet"
);
}
if
(
credentials
!=
null
)
{
commands
.
add
(
"--username"
);
commands
.
add
(
credentials
.
getUsername
());
commands
.
add
(
"--password"
);
commands
.
add
(
credentials
.
getPassword
());
commands
.
add
(
"--authenticationDatabase"
);
commands
.
add
(
credentials
.
getAuthDatabase
());
}
if
(
eval
!=
null
&&
!
eval
.
isEmpty
())
{
commands
.
add
(
"--eval"
);
commands
.
add
(
eval
);
}
if
(
script
!=
null
&&
!
script
.
isEmpty
())
{
commands
.
add
(
script
);
}
final
String
[]
cmdarray
=
commands
.
toArray
(
new
String
[
commands
.
size
()]);
final
Process
child
=
Runtime
.
getRuntime
().
exec
(
cmdarray
);
final
int
returnCode
=
child
.
waitFor
();
final
String
stdOut
=
readStream
(
child
.
getInputStream
());
if
(
returnCode
!=
0
)
{
throw
new
RuntimeException
(
"Failed to execute MongoDB shell with arguments: "
+
Arrays
.
toString
(
cmdarray
)
+
".\n"
+
stdOut
);
}
return
stdOut
;
}
/**
* Converts input stream to String containing lines separated by \n
*/
private
String
readStream
(
final
InputStream
stream
)
{
final
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
stream
,
StandardCharsets
.
UTF_8
));
final
StringJoiner
builder
=
new
StringJoiner
(
"\n"
);
reader
.
lines
().
forEach
(
builder:
:
add
);
return
builder
.
toString
();
}
}
test/src/main/java/com/github/variety/Variety.java
View file @
a1a10a63
...
...
@@ -6,17 +6,12 @@ import com.github.variety.validator.ResultsValidator;
import
com.mongodb.DB
;
import
com.mongodb.DBCollection
;
import
com.mongodb.MongoClient
;
import
com.mongodb.ServerAddress
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.net.UnknownHostException
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.StringJoiner
;
/**
...
...
@@ -42,6 +37,8 @@ public class Variety {
private
final
String
inputCollection
;
private
final
MongoClient
mongoClient
;
private
final
Credentials
credentials
;
private
Integer
limit
;
private
Integer
maxDepth
;
private
String
query
;
...
...
@@ -57,9 +54,19 @@ public class Variety {
* @throws UnknownHostException Thrown when fails connection do default host and port of MongoDB
*/
public
Variety
(
final
String
database
,
final
String
collection
)
throws
UnknownHostException
{
this
.
inputDatabase
=
database
;
this
.
inputCollection
=
collection
;
this
(
database
,
collection
,
null
);
}
public
Variety
(
final
String
inputDatabase
,
final
String
inputCollection
,
final
Credentials
credentials
)
throws
UnknownHostException
{
this
.
inputDatabase
=
inputDatabase
;
this
.
inputCollection
=
inputCollection
;
this
.
credentials
=
credentials
;
if
(
credentials
==
null
)
{
this
.
mongoClient
=
new
MongoClient
();
}
else
{
this
.
mongoClient
=
new
MongoClient
(
new
ServerAddress
(
"localhost"
),
Arrays
.
asList
(
credentials
.
getMongoCredential
()));
}
}
/**
...
...
@@ -138,28 +145,11 @@ public class Variety {
* Executes mongo shell with configured variety options and variety.js script in path.
* @return Stdout of variety.js
*/
private
String
runAnalysis
()
throws
IOException
,
InterruptedException
{
final
List
<
String
>
commands
=
new
ArrayList
<>();
commands
.
add
(
"mongo"
);
commands
.
add
(
this
.
inputDatabase
);
if
(
quiet
)
{
commands
.
add
(
"--quiet"
);
}
commands
.
add
(
"--eval"
);
commands
.
add
(
buildParams
());
commands
.
add
(
getVarietyPath
());
final
String
[]
cmdarray
=
commands
.
toArray
(
new
String
[
commands
.
size
()]);
final
Process
child
=
Runtime
.
getRuntime
().
exec
(
cmdarray
);
final
int
returnCode
=
child
.
waitFor
();
final
String
stdOut
=
readStream
(
child
.
getInputStream
());
if
(
returnCode
!=
0
)
{
throw
new
RuntimeException
(
"Failed to execute variety.js with arguments: "
+
Arrays
.
toString
(
cmdarray
)
+
".\n"
+
stdOut
);
}
System
.
out
.
println
(
stdOut
);
return
stdOut
;
public
String
runAnalysis
()
throws
IOException
,
InterruptedException
{
final
MongoShell
mongoShell
=
new
MongoShell
(
inputDatabase
,
credentials
,
buildParams
(),
getVarietyPath
(),
quiet
);
final
String
result
=
mongoShell
.
execute
();
System
.
out
.
println
(
result
);
return
result
;
}
public
ResultsValidator
runJsonAnalysis
()
throws
IOException
,
InterruptedException
{
...
...
@@ -214,13 +204,5 @@ public class Variety {
return
Paths
.
get
(
this
.
getClass
().
getResource
(
"/"
).
getFile
()).
getParent
().
getParent
().
getParent
().
resolve
(
"variety.js"
).
toString
();
}
/**
* Converts input stream to String containing lines separated by \n
*/
private
String
readStream
(
final
InputStream
stream
)
{
final
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
stream
,
StandardCharsets
.
UTF_8
));
final
StringJoiner
builder
=
new
StringJoiner
(
"\n"
);
reader
.
lines
().
forEach
(
builder:
:
add
);
return
builder
.
toString
();
}
}
test/src/test/java/com/github/variety/test/LimitedAccessTest.java
0 → 100644
View file @
a1a10a63
package
com
.
github
.
variety
.
test
;
import
com.github.variety.Credentials
;
import
com.github.variety.MongoShell
;
import
com.github.variety.Variety
;
import
com.github.variety.validator.ResultsValidator
;
import
com.mongodb.MongoClient
;
import
com.mongodb.ServerAddress
;
import
org.junit.After
;
import
org.junit.Assert
;
import
org.junit.Before
;
import
org.junit.Test
;
import
java.io.IOException
;
import
java.util.Arrays
;
/**
* Tests, if variety can return results for user with read only access to analyzed database (without permission to list
* all other dbs / collections, without permission to persist results).
*/
public
class
LimitedAccessTest
{
private
Variety
variety
;
private
MongoClient
adminConnection
;
@Before
public
void
setUp
()
throws
Exception
{
// create admin user (expects empty users table => no auth used for this connection)
createUser
(
null
,
Credentials
.
ADMIN
);
// create limited user
createUser
(
Credentials
.
ADMIN
,
Credentials
.
USER
);
// connect with admin credentials
adminConnection
=
new
MongoClient
(
new
ServerAddress
(
"localhost"
),
Arrays
.
asList
(
Credentials
.
ADMIN
.
getMongoCredential
()));
// create sample collection (logged admin user, writes to test DB)
adminConnection
.
getDB
(
"test"
).
getCollection
(
"users"
).
insert
(
SampleData
.
getDocuments
());
// initialize variety with limited user credentials, connects to test/users collection
variety
=
new
Variety
(
"test"
,
"users"
,
Credentials
.
USER
);
}
private
void
createUser
(
final
Credentials
loginCredentials
,
final
Credentials
userToCreate
)
throws
IOException
,
InterruptedException
{
final
MongoShell
shell
=
new
MongoShell
(
userToCreate
.
getAuthDatabase
(),
loginCredentials
,
"db.addUser("
+
userToCreate
.
getUserDocument
()
+
")"
,
null
,
false
);
System
.
out
.
println
(
shell
.
execute
());
}
@After
public
void
tearDown
()
throws
Exception
{
adminConnection
.
getDB
(
"test"
).
getCollection
(
"users"
).
drop
();
// remove both users from admin (auth) database. Caution, order is important - first delete user, then admin
System
.
out
.
println
(
new
MongoShell
(
"test"
,
Credentials
.
ADMIN
,
"db.removeUser('"
+
Credentials
.
USER
.
getUsername
()
+
"')"
,
null
,
false
).
execute
());
System
.
out
.
println
(
new
MongoShell
(
"admin"
,
Credentials
.
ADMIN
,
"db.removeUser('"
+
Credentials
.
ADMIN
.
getUsername
()
+
"')"
,
null
,
false
).
execute
());
}
/**
* Validate correct results read from JSON standard output, limited user connection provided
*/
@Test
public
void
verifyBasicResultsJson
()
throws
Exception
{
validate
(
variety
.
runJsonAnalysis
());
}
@Test
public
void
verifyBasicResultsAscii
()
throws
Exception
{
final
String
stdout
=
variety
.
withPersistResults
(
false
).
withQuiet
(
true
).
runAnalysis
();
Assert
.
assertEquals
(
SampleData
.
EXPECTED_DATA_ASCII_TABLE
,
stdout
);
}
@Test
public
void
testNotFoundDatabaseForAdmin
()
throws
Exception
{
final
Variety
adminVariety
=
new
Variety
(
"foo"
,
"users"
,
Credentials
.
ADMIN
);
try
{
adminVariety
.
runAnalysis
();
Assert
.
fail
(
"Should throw exception"
);
}
catch
(
final
Exception
e
)
{
System
.
out
.
println
(
e
);
final
String
messageVersion24
=
"The collection specified (users) in the database specified (foo) does not exist or is empty"
;
final
String
messageVersion26
=
"The database specified (foo) does not exist"
;
Assert
.
assertTrue
(
e
.
getMessage
().
contains
(
messageVersion24
)
||
e
.
getMessage
().
contains
(
messageVersion26
));
}
}
@Test
public
void
testNotFoundCollectionForAdmin
()
throws
Exception
{
final
Variety
adminVariety
=
new
Variety
(
"test"
,
"bar"
,
Credentials
.
ADMIN
);
try
{
adminVariety
.
runAnalysis
();
Assert
.
fail
(
"Should throw exception"
);
}
catch
(
final
Exception
e
)
{
Assert
.
assertTrue
(
e
.
getMessage
().
contains
(
"The collection specified (bar) in the database specified (test) does not exist or is empty."
));
Assert
.
assertTrue
(
e
.
getMessage
().
contains
(
"Possible collection options for database specified:"
));
}
}
@Test
public
void
testNotFoundCollectionForUser
()
throws
Exception
{
final
Variety
adminVariety
=
new
Variety
(
"test"
,
"bar"
,
Credentials
.
USER
);
try
{
adminVariety
.
runAnalysis
();
Assert
.
fail
(
"Should throw exception"
);
}
catch
(
final
Exception
e
)
{
Assert
.
assertTrue
(
e
.
getMessage
().
contains
(
"The collection specified (bar) in the database specified (test) does not exist or is empty."
));
Assert
.
assertTrue
(
e
.
getMessage
().
contains
(
"Possible collection options for database specified:"
));
}
}
@Test
public
void
testNotFoundDbForUser
()
throws
Exception
{
final
Variety
adminVariety
=
new
Variety
(
"foo"
,
"users"
,
Credentials
.
USER
);
try
{
adminVariety
.
runAnalysis
();
Assert
.
fail
(
"Should throw exception"
);
}
catch
(
final
Exception
e
)
{
Assert
.
assertTrue
(
"Exception should contain info about not authorized access, full message is: '"
+
e
.
getMessage
()
+
"'"
,
e
.
getMessage
().
contains
(
"not authorized"
));
}
}
private
void
validate
(
final
ResultsValidator
analysis
)
{
analysis
.
validate
(
"_id"
,
5
,
100
,
"ObjectId"
);
analysis
.
validate
(
"name"
,
5
,
100
,
"String"
);
analysis
.
validate
(
"bio"
,
3
,
60
,
"String"
);
analysis
.
validate
(
"pets"
,
2
,
40
,
"String"
,
"Array"
);
analysis
.
validate
(
"someBinData"
,
1
,
20
,
"BinData-old"
);
analysis
.
validate
(
"someWeirdLegacyKey"
,
1
,
20
,
"String"
);
}
}
variety.js
View file @
a1a10a63
...
...
@@ -25,24 +25,26 @@ if (typeof db_name === 'string') {
db
=
db
.
getMongo
().
getDB
(
db_name
);
}
db
.
adminCommand
(
'listDatabases'
).
databases
.
forEach
(
function
(
d
){
var
knownDatabases
=
db
.
adminCommand
(
'listDatabases'
).
databases
;
if
(
typeof
knownDatabases
!==
'undefined'
)
{
// not authorized user receives error response (json) without databases key
knownDatabases
.
forEach
(
function
(
d
){
if
(
db
.
getSisterDB
(
d
.
name
).
getCollectionNames
().
length
>
0
)
{
dbs
.
push
(
d
.
name
);
}
if
(
db
.
getSisterDB
(
d
.
name
).
getCollectionNames
().
length
===
0
)
{
emptyDbs
.
push
(
d
.
name
);
}
});
});
if
(
emptyDbs
.
indexOf
(
db
.
getName
())
!==
-
1
)
{
if
(
emptyDbs
.
indexOf
(
db
.
getName
())
!==
-
1
)
{
throw
'The database specified ('
+
db
+
') is empty.
\
n'
+
'Possible database options are: '
+
dbs
.
join
(
', '
)
+
'.'
;
}
}
if
(
dbs
.
indexOf
(
db
.
getName
())
===
-
1
)
{
if
(
dbs
.
indexOf
(
db
.
getName
())
===
-
1
)
{
throw
'The database specified ('
+
db
+
') does not exist.
\
n'
+
'Possible database options are: '
+
dbs
.
join
(
', '
)
+
'.'
;
}
}
var
collNames
=
db
.
getCollectionNames
().
join
(
', '
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment