geo locate user IPs
parent
4daca04896
commit
90db51f9aa
|
@ -104,12 +104,22 @@
|
|||
</table>
|
||||
}
|
||||
<div class="listings clearfix">
|
||||
<div style="float: left; margin-left: 1%; width: 21%;">
|
||||
<strong>@spy.ips.size IP addresses</strong> <ul>@spy.ips.sorted.map { ip =>
|
||||
<li@{ip._2.??(Html(" class='blocked'"))}>@ip._1</li>
|
||||
}</ul>
|
||||
<div class="spy_ips">
|
||||
<strong>@spy.ips.size IP addresses</strong> <ul>@spy.ipsByLocations.map {
|
||||
case (location, ips) => {
|
||||
<li>
|
||||
<p>@location</p>
|
||||
<ul>
|
||||
@ips.map { ip =>
|
||||
<li@if(ip.blocked) { class="blocked" }>@ip.ip</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div style="margin-left: 24%;">
|
||||
<div class="spy_uas">
|
||||
<strong>@spy.uas.size User agent(s)</strong> <ul>@spy.uas.sorted.map { ua =>
|
||||
<li>@ua</li>
|
||||
}</ul>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
mkdir -p data
|
||||
cd data
|
||||
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
|
||||
gunzip GeoLiteCity.dat.gz
|
||||
rm GeoLiteCity.dat.gz
|
||||
cd -
|
|
@ -24,6 +24,8 @@ final class Env(
|
|||
val FirewallCollectionFirewall = config getString "firewall.collection.firewall"
|
||||
val FirewallCachedIpsTtl = config duration "firewall.cached.ips.ttl"
|
||||
val FloodDuration = config duration "flood.duration"
|
||||
val GeoIPFile = config getString "geoip.file"
|
||||
val GeoIPCacheSize = config getInt "geoip.cache_size"
|
||||
}
|
||||
import settings._
|
||||
|
||||
|
@ -40,7 +42,11 @@ final class Env(
|
|||
|
||||
lazy val forms = new DataForm(captcher = captcher)
|
||||
|
||||
lazy val userSpy = UserSpy(firewall) _
|
||||
private lazy val geoIP = new GeoIP(
|
||||
file = new java.io.File(GeoIPFile),
|
||||
cacheSize = GeoIPCacheSize)
|
||||
|
||||
lazy val userSpy = UserSpy(firewall, geoIP) _
|
||||
|
||||
lazy val disconnect = Store disconnect _
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package lila.security
|
||||
|
||||
import com.snowplowanalytics.maxmind.geoip.{ IpGeo, IpLocation }
|
||||
|
||||
import lila.memo.AsyncCache
|
||||
import scala.concurrent.Future
|
||||
|
||||
private[security] final class GeoIP(file: java.io.File, cacheSize: Int) {
|
||||
|
||||
private val ipgeo = new IpGeo(dbFile = file, memCache = false, lruCache = 0)
|
||||
|
||||
private val cache = AsyncCache(
|
||||
f = (ip: String) => Future { ipgeo getLocation ip },
|
||||
maxCapacity = cacheSize)
|
||||
|
||||
def apply(ip: String): Future[Location] =
|
||||
cache(ip) map (_.fold(Location.unknown)(Location.apply))
|
||||
}
|
||||
|
||||
case class Location(
|
||||
country: String,
|
||||
region: Option[String],
|
||||
city: Option[String]) {
|
||||
|
||||
def comparable = (country, ~region, ~city)
|
||||
|
||||
override def toString = List(city, region, country.some).flatten mkString ", "
|
||||
}
|
||||
|
||||
object Location {
|
||||
|
||||
val unknown = Location("Solar System", none, none)
|
||||
|
||||
def apply(ipLoc: IpLocation): Location =
|
||||
Location(ipLoc.countryName, ipLoc.region, ipLoc.city)
|
||||
}
|
|
@ -14,25 +14,33 @@ import lila.user.{ User, UserRepo }
|
|||
import tube.storeTube
|
||||
|
||||
case class UserSpy(
|
||||
ips: List[(String, Boolean)],
|
||||
ips: List[UserSpy.IPData],
|
||||
uas: List[String],
|
||||
otherUsers: List[User]) {
|
||||
|
||||
def ipStrings = ips map (_._1)
|
||||
def ipStrings = ips map (_.ip)
|
||||
|
||||
def ipsByLocations: List[(Location, List[UserSpy.IPData])] =
|
||||
ips.sortBy(_.ip).groupBy(_.location).toList.sortBy(_._1.comparable)
|
||||
}
|
||||
|
||||
private[security] object UserSpy {
|
||||
object UserSpy {
|
||||
|
||||
type IP = String
|
||||
|
||||
private[security] def apply(firewall: Firewall)(userId: String): Fu[UserSpy] = for {
|
||||
case class IPData(ip: IP, blocked: Boolean, location: Location)
|
||||
|
||||
private[security] def apply(firewall: Firewall, geoIP: GeoIP)(userId: String): Fu[UserSpy] = for {
|
||||
user ← UserRepo named userId flatten "[spy] user not found"
|
||||
objs ← $find(Json.obj("user" -> user.id))
|
||||
users ← explore(Set(user), Set.empty, Set(user))
|
||||
ips = objs.map(_ str "ip").flatten.distinct
|
||||
ips = objs.flatMap(_ str "ip").distinct
|
||||
blockedIps ← (ips map firewall.blocksIp).sequenceFu
|
||||
locations <- (ips map geoIP.apply).sequenceFu
|
||||
users ← explore(Set(user), Set.empty, Set(user))
|
||||
} yield UserSpy(
|
||||
ips = ips zip blockedIps,
|
||||
ips = ips zip blockedIps zip locations map {
|
||||
case ((ip, blocked), location) => IPData(ip, blocked, location)
|
||||
},
|
||||
uas = objs.map(_ str "ua").flatten.distinct,
|
||||
otherUsers = (users + user).toList.sortBy(_.createdAt)
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= Seq(
|
||||
scalaz, scalalib, hasher, config, apache, scalaTime,
|
||||
csv, jgit, actuarius, elastic4s, findbugs, RM,
|
||||
PRM, spray.caching),
|
||||
PRM, spray.caching, maxmind),
|
||||
scalacOptions := compilerOptions,
|
||||
sources in doc in Compile := List(),
|
||||
incOptions := incOptions.value.withNameHashing(true),
|
||||
|
@ -145,7 +145,7 @@ object ApplicationBuild extends Build {
|
|||
|
||||
lazy val security = project("security", Seq(common, hub, db, user)).settings(
|
||||
libraryDependencies ++= provided(
|
||||
play.api, RM, PRM, spray.caching)
|
||||
play.api, RM, PRM, maxmind)
|
||||
)
|
||||
|
||||
lazy val relation = project("relation", Seq(common, db, memo, hub, user, game)).settings(
|
||||
|
|
|
@ -45,6 +45,8 @@ object Dependencies {
|
|||
val elastic4s = "com.sksamuel.elastic4s" %% "elastic4s" % "1.0.1.0"
|
||||
val RM = "org.reactivemongo" %% "reactivemongo" % "0.10.0"
|
||||
val PRM = "org.reactivemongo" %% "play2-reactivemongo" % "0.10.2"
|
||||
val maxmind = "com.snowplowanalytics" %% "scala-maxmind-geoip" % "0.0.5"
|
||||
|
||||
object play {
|
||||
val version = "2.2.1"
|
||||
val api = "com.typesafe.play" %% "play" % version
|
||||
|
|
|
@ -41,16 +41,13 @@ div.user_show .relation_actions {
|
|||
div.user_show .relation_actions form {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.user_show div.meat {
|
||||
height: 325px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.engine_warning {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sub_ratings.sep {
|
||||
margin-top: 2.5em;
|
||||
}
|
||||
|
@ -62,7 +59,6 @@ div.sub_ratings h3 {
|
|||
#site_header div.sub_ratings strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.user_show .rating_history {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
|
@ -76,7 +72,6 @@ div.user_show .rating_history span {
|
|||
top: 164px;
|
||||
left: 232px;
|
||||
}
|
||||
|
||||
div.user_show .user-infos {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -87,7 +82,6 @@ div.user_show .user-infos {
|
|||
width: 310px;
|
||||
height: 325px;
|
||||
}
|
||||
|
||||
div.user_show .name,
|
||||
div.user_show .bio,
|
||||
div.user_show .boxed_data,
|
||||
|
@ -100,12 +94,10 @@ div.user_show .teams {
|
|||
div.user_show .bio {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.user_show .teams a {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
div.user_show div.boxed_data {
|
||||
width: 292px;
|
||||
padding: 5px 8px;
|
||||
|
@ -117,11 +109,9 @@ div.user_show div.boxed_data a.user_link {
|
|||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.user_show div.games {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.user_show .profile {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
@ -179,6 +169,25 @@ div.user_show .mod_zone .listings > div {
|
|||
max-height: 20em;
|
||||
overflow: auto;
|
||||
}
|
||||
div.user_show .mod_zone .spy_ips {
|
||||
float: left;
|
||||
margin-left: 1%;
|
||||
width: 31%;
|
||||
}
|
||||
div.user_show .mod_zone .spy_ips > ul > li {
|
||||
list-style: inside disc;
|
||||
}
|
||||
div.user_show .mod_zone .spy_ips ul p {
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
}
|
||||
div.user_show .mod_zone .spy_ips li li {
|
||||
margin-left: 1em;
|
||||
font-family: monospace;
|
||||
}
|
||||
div.user_show .mod_zone .spy_uas {
|
||||
margin-left: 34%;
|
||||
}
|
||||
div.user_show .mod_zone .listings .blocked {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
|
|
Loading…
Reference in New Issue