mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 19:56:59 +00:00
Merge branch 'develop' of https://github.com/OpenBankProject/OBP-API into develop
This commit is contained in:
commit
e3163876d4
@ -299,7 +299,11 @@ object DirectLogin extends RestHelper with Loggable {
|
||||
case p: String => p
|
||||
case _ => "error"
|
||||
}
|
||||
val userId = OBPUser.getUserId(username, password)
|
||||
var userId:Long = OBPUser.getUserId(username, password).getOrElse(0)
|
||||
if (userId == 0) {
|
||||
OBPUser.externalUserHelper(directLoginParameters.getOrElse("username", ""), directLoginParameters.getOrElse("password", ""))
|
||||
userId = OBPUser.getUserId(username, password).getOrElse(0)
|
||||
}
|
||||
userId
|
||||
}
|
||||
|
||||
|
||||
@ -483,6 +483,24 @@ object OAuthHandshake extends RestHelper with Loggable {
|
||||
nonceSaved && tokenSaved
|
||||
}
|
||||
|
||||
def getConsumer: List[Consumer] = {
|
||||
val httpMethod = S.request match {
|
||||
case Full(r) => r.request.method
|
||||
case _ => "GET"
|
||||
}
|
||||
val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod)
|
||||
import code.model.Token
|
||||
val consumer: Option[Consumer] = for {
|
||||
tokenId: String <- oAuthParameters.get("oauth_token")
|
||||
token: Token <- Token.find(By(Token.key, tokenId))
|
||||
consumer: Consumer <- token.consumerId.foreign
|
||||
} yield {
|
||||
consumer
|
||||
}
|
||||
consumer.toList
|
||||
}
|
||||
|
||||
|
||||
def getUser : Box[User] = {
|
||||
val httpMethod = S.request match {
|
||||
case Full(r) => r.request.method
|
||||
|
||||
@ -173,7 +173,14 @@ object APIUtil extends Loggable {
|
||||
// TODO This should use Elastic Search or Kafka not an RDBMS
|
||||
val u = user.orNull
|
||||
val userId = if (u != null) u.userId else "null"
|
||||
APIMetrics.apiMetrics.vend.saveMetric(userId, S.uriAndQueryString.getOrElse(""), (now: TimeSpan))
|
||||
val userName = if (u != null) u.name else "null"
|
||||
var appName = "null"
|
||||
var developerEmail = "null"
|
||||
for (c <- getConsumer) {
|
||||
appName = c.name.get
|
||||
developerEmail = c.developerEmail.get
|
||||
}
|
||||
APIMetrics.apiMetrics.vend.saveMetric(userId, S.uriAndQueryString.getOrElse(""), (now: TimeSpan), userName, appName, developerEmail)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -68,36 +68,19 @@ object KafkaMappedConnector extends Connector with CreateViewImpls with Loggable
|
||||
}
|
||||
|
||||
def setAccountOwner(owner : String, account: KafkaInboundAccount) : Unit = {
|
||||
val apiUserOwner = APIUser.findAll.find(user => owner == user.emailAddress)
|
||||
apiUserOwner match {
|
||||
case Some(o) => {
|
||||
if ( ! accountOwnerExists(o, account)) {
|
||||
MappedAccountHolder.createMappedAccountHolder(o.apiId.value, account.bank, account.id, "KafkaMappedConnector")
|
||||
}
|
||||
if (account.owners.contains(owner)) {
|
||||
val apiUserOwner = APIUser.findAll.find(user => owner == user.emailAddress)
|
||||
apiUserOwner match {
|
||||
case Some(o) => {
|
||||
if ( ! accountOwnerExists(o, account)) {
|
||||
MappedAccountHolder.createMappedAccountHolder(o.apiId.value, account.bank, account.id, "KafkaMappedConnector")
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
//This shouldn't happen as OBPUser should generate the APIUsers when saved
|
||||
logger.error(s"api user(s) with email $owner not found.")
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
//This shouldn't happen as OBPUser should generate the APIUsers when saved
|
||||
logger.error(s"api user(s) with email $owner not found.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def updateAccountViews(user: APIUser, account: KafkaInboundAccount ) = {
|
||||
for {
|
||||
email <- tryo{user.emailAddress}
|
||||
views <- tryo{createSaveableViews(account, account.owners.contains(email))}
|
||||
existing_views <- tryo {Views.views.vend.views(new KafkaBankAccount(account))}
|
||||
} yield {
|
||||
views.foreach(_.save())
|
||||
views.map(_.value).foreach(v => {
|
||||
Views.views.vend.addPermission(v.uid, user)
|
||||
logger.info(s"------------> updated view ${v.uid} for apiuser ${user} and account ${account}")
|
||||
})
|
||||
existing_views.filterNot(_.users.contains(user)).foreach (v => {
|
||||
Views.views.vend.addPermission(v.uid, user)
|
||||
logger.info(s"------------> added apiuser ${user} to view ${v.uid} for account ${account}")
|
||||
})
|
||||
setAccountOwner(email, account)
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +104,7 @@ object KafkaMappedConnector extends Connector with CreateViewImpls with Loggable
|
||||
views.foreach(_.save())
|
||||
views.map(_.value).foreach(v => {
|
||||
Views.views.vend.addPermission(v.uid, user)
|
||||
logger.info(s"------------> added view ${v.uid} for apiuser ${user} and account ${acc}")
|
||||
logger.info(s"------------> updated view ${v.uid} for apiuser ${user} and account ${acc}")
|
||||
})
|
||||
existing_views.filterNot(_.users.contains(user)).foreach (v => {
|
||||
Views.views.vend.addPermission(v.uid, user)
|
||||
@ -291,14 +274,7 @@ object KafkaMappedConnector extends Connector with CreateViewImpls with Loggable
|
||||
val r = {
|
||||
cachedAccount.getOrElseUpdate( argList.toString, () => process(reqId, "getBankAccount", argList).extract[KafkaInboundAccount])
|
||||
}
|
||||
val res = new KafkaBankAccount(r)
|
||||
for {
|
||||
e <- tryo{OBPUser.getCurrentUserUsername}
|
||||
u <- OBPUser.getApiUserByEmail(e)
|
||||
} yield {
|
||||
updateAccountViews(u, r)
|
||||
}
|
||||
Full(res)
|
||||
Full(new KafkaBankAccount(r))
|
||||
}
|
||||
|
||||
override def getBankAccounts(accts: List[(BankId, AccountId)]): List[KafkaBankAccount] = {
|
||||
@ -314,17 +290,7 @@ object KafkaMappedConnector extends Connector with CreateViewImpls with Loggable
|
||||
val r = {
|
||||
cachedAccounts.getOrElseUpdate( argList.toString, () => process(reqId, "getBankAccounts", argList).extract[List[KafkaInboundAccount]])
|
||||
}
|
||||
val res = r.map { t =>
|
||||
val a = new KafkaBankAccount(t)
|
||||
for {
|
||||
e <- tryo{OBPUser.getCurrentUserUsername}
|
||||
u <- OBPUser.getApiUserByEmail(e)
|
||||
} yield {
|
||||
updateAccountViews(u, t)
|
||||
}
|
||||
a
|
||||
}
|
||||
res
|
||||
r.map { t => new KafkaBankAccount(t) }
|
||||
}
|
||||
|
||||
private def getAccountByNumber(bankId : BankId, number : String) : Box[AccountType] = {
|
||||
@ -340,14 +306,7 @@ object KafkaMappedConnector extends Connector with CreateViewImpls with Loggable
|
||||
val r = {
|
||||
cachedAccount.getOrElseUpdate( argList.toString, () => process(reqId, "getBankAccount", argList).extract[KafkaInboundAccount])
|
||||
}
|
||||
val res = new KafkaBankAccount(r)
|
||||
for {
|
||||
e <- tryo{OBPUser.getCurrentUserUsername}
|
||||
u <- OBPUser.getApiUserByEmail(e)
|
||||
} yield {
|
||||
updateAccountViews(u, r)
|
||||
}
|
||||
Full(res)
|
||||
Full(new KafkaBankAccount(r))
|
||||
}
|
||||
|
||||
def getOtherBankAccount(thisAccountBankId : BankId, thisAccountId : AccountId, metadata : OtherBankAccountMetadata) : Box[OtherBankAccount] = {
|
||||
|
||||
@ -34,11 +34,11 @@ object APIMetrics extends SimpleInjector {
|
||||
|
||||
trait APIMetrics {
|
||||
|
||||
def saveMetric(userId: String, url : String, date : Date) : Unit
|
||||
def saveMetric(userId: String, url : String, date : Date, userName: String, appName: String, developerEmail: String) : Unit
|
||||
|
||||
def saveMetric(url : String, date : Date) : Unit ={
|
||||
//TODO: update all places calling old function before removing this
|
||||
saveMetric ("TODO: userId", url, date)
|
||||
saveMetric ("TODO: userId", url, date, "TODO: userName", "TODO: appName", "TODO: developerEmail")
|
||||
}
|
||||
|
||||
//TODO: ordering of list? should this be by date? currently not enforced
|
||||
@ -57,4 +57,7 @@ trait APIMetric {
|
||||
def getUrl() : String
|
||||
def getDate() : Date
|
||||
def getUserId() : String
|
||||
def getUserName() : String
|
||||
def getAppName : String
|
||||
def getDeveloperEmail() : String
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@ object ElasticsearchMetrics extends APIMetrics {
|
||||
|
||||
val es = new elasticsearchMetrics
|
||||
|
||||
override def saveMetric(userId: String, url: String, date: Date): Unit = {
|
||||
override def saveMetric(userId: String, url: String, date: Date, userName: String, appName: String, developerEmail: String): Unit = {
|
||||
if (Props.getBool("allow_elasticsearch", false) && Props.getBool("allow_elasticsearch_metrics", false) ) {
|
||||
|
||||
es.indexMetric(userId, url, date)
|
||||
es.indexMetric(userId, url, date, userName, appName, developerEmail)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,8 +7,8 @@ import net.liftweb.mapper._
|
||||
|
||||
object MappedMetrics extends APIMetrics {
|
||||
|
||||
override def saveMetric(userId: String, url: String, date: Date): Unit = {
|
||||
MappedMetric.create.url(url).date(date).save
|
||||
override def saveMetric(userId: String, url: String, date: Date, userName: String, appName: String, developerEmail: String): Unit = {
|
||||
MappedMetric.create.url(url).date(date).userName(userName).appName(appName).developerEmail(developerEmail).save
|
||||
}
|
||||
|
||||
override def getAllGroupedByUserId(): Map[String, List[APIMetric]] = {
|
||||
@ -34,13 +34,19 @@ class MappedMetric extends APIMetric with LongKeyedMapper[MappedMetric] with IdP
|
||||
object userId extends DefaultStringField(this)
|
||||
object url extends DefaultStringField(this)
|
||||
object date extends MappedDateTime(this)
|
||||
object userName extends DefaultStringField(this)
|
||||
object appName extends DefaultStringField(this)
|
||||
object developerEmail extends DefaultStringField(this)
|
||||
|
||||
|
||||
override def getUrl(): String = url.get
|
||||
override def getDate(): Date = date.get
|
||||
override def getUserId(): String = userId.get
|
||||
override def getUserName(): String = userName.get
|
||||
override def getAppName(): String = appName.get
|
||||
override def getDeveloperEmail(): String = developerEmail.get
|
||||
}
|
||||
|
||||
object MappedMetric extends MappedMetric with LongKeyedMetaMapper[MappedMetric] {
|
||||
override def dbIndexes = Index(userId) :: Index(url) :: Index(date) :: super.dbIndexes
|
||||
override def dbIndexes = Index(userId) :: Index(url) :: Index(date) :: Index(userName) :: Index(appName) :: Index(developerEmail) :: super.dbIndexes
|
||||
}
|
||||
|
||||
@ -43,19 +43,28 @@ import net.liftweb.record.field.StringField
|
||||
object userId extends StringField(this,255)
|
||||
object url extends StringField(this,255)
|
||||
object date extends DateField(this)
|
||||
object userName extends StringField(this,255)
|
||||
object appName extends StringField(this,255)
|
||||
object developerEmail extends StringField(this,255)
|
||||
|
||||
def getUrl() = url.get
|
||||
def getDate() = date.get
|
||||
def getUserId() = userId.get
|
||||
def getUserName(): String = userName.get
|
||||
def getAppName(): String = appName.get
|
||||
def getDeveloperEmail(): String = developerEmail.get
|
||||
}
|
||||
|
||||
private object MongoAPIMetric extends MongoAPIMetric with MongoMetaRecord[MongoAPIMetric] with APIMetrics {
|
||||
|
||||
def saveMetric(userId: String, url : String, date : Date) : Unit = {
|
||||
def saveMetric(userId: String, url : String, date : Date, userName: String, appName: String, developerEmail: String) : Unit = {
|
||||
MongoAPIMetric.createRecord.
|
||||
userId(userId).
|
||||
url(url).
|
||||
date(date).
|
||||
userName(userName).
|
||||
appName(appName).
|
||||
developerEmail(developerEmail).
|
||||
save
|
||||
}
|
||||
|
||||
|
||||
@ -279,23 +279,25 @@ import net.liftweb.util.Helpers._
|
||||
|
||||
// What if we just want to return the userId without sending username/password??
|
||||
|
||||
def getUserId(username: String, password: String): Long = {
|
||||
def getUserId(username: String, password: String): Box[Long] = {
|
||||
findUserByUserName(username) match {
|
||||
case Full(user) => {
|
||||
if (user.validated_? &&
|
||||
user.getProvider() == Props.get("hostname","") &&
|
||||
user.testPassword(Full(password)))
|
||||
{
|
||||
user.id.toLong
|
||||
Full(user.id.toLong)
|
||||
}
|
||||
else {
|
||||
Props.get("connector").openOrThrowException("no connector set") match {
|
||||
case "kafka" => getUserFromKafka(username, password).get.id.toLong
|
||||
case _ => 0
|
||||
case "kafka" =>
|
||||
for { kafkaUser <- getUserFromKafka(username, password)
|
||||
kafkaUserId <- tryo{kafkaUser.id} } yield kafkaUserId.toLong
|
||||
case _ => Full(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
case _ => 0
|
||||
case _ => Full(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,7 +365,7 @@ import net.liftweb.util.Helpers._
|
||||
val redir = loginRedirect.get match {
|
||||
case Full(url) =>
|
||||
loginRedirect(Empty)
|
||||
url
|
||||
url
|
||||
case _ =>
|
||||
homePage
|
||||
}
|
||||
@ -390,26 +392,13 @@ import net.liftweb.util.Helpers._
|
||||
homePage
|
||||
}
|
||||
for {
|
||||
username_ <- S.param("username")
|
||||
password_ <- S.param("password")
|
||||
user_ <- getUserFromKafka(username_, password_)
|
||||
user_ <- externalUserHelper(S.param("username").getOrElse(""), S.param("password").getOrElse(""))
|
||||
} yield {
|
||||
if (user != null) {
|
||||
logUserIn(user_, () => {
|
||||
for {
|
||||
u <- APIUser.find(By(APIUser.email, user_.email))
|
||||
v <- tryo {
|
||||
KafkaMappedConnector.updateUserAccountViews(u)
|
||||
}
|
||||
}
|
||||
S.notice(S.?("logged.in"))
|
||||
preLoginState()
|
||||
S.redirectTo(redir)
|
||||
})
|
||||
user_
|
||||
}
|
||||
else
|
||||
userLoginFailed
|
||||
logUserIn(user_, () => {
|
||||
S.notice(S.?("logged.in"))
|
||||
preLoginState()
|
||||
S.redirectTo(redir)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -422,6 +411,19 @@ import net.liftweb.util.Helpers._
|
||||
"submit" -> loginSubmitButton(S.?("log.in")))
|
||||
}
|
||||
|
||||
def externalUserHelper(username: String, password: String): Box[OBPUser] = {
|
||||
if (Props.get("connector").openOrThrowException("no connector set") == "kafka") {
|
||||
for {
|
||||
user <- getUserFromKafka(username, password)
|
||||
u <- APIUser.find(By(APIUser.email, user.email))
|
||||
v <- tryo {KafkaMappedConnector.updateUserAccountViews(u)}
|
||||
} yield {
|
||||
user
|
||||
}
|
||||
} else Full(OBPUser)
|
||||
}
|
||||
|
||||
|
||||
//overridden to allow redirect to loginRedirect after signup. This is mostly to allow
|
||||
// loginFirst menu items to work if the user doesn't have an account. Without this,
|
||||
// if a user tries to access a logged-in only page, and then signs up, they don't get redirected
|
||||
|
||||
@ -142,7 +142,10 @@ class elasticsearchMetrics extends elasticsearch {
|
||||
"request" as (
|
||||
"userId" typed StringType,
|
||||
"url" typed StringType,
|
||||
"date" typed DateType
|
||||
"date" typed DateType,
|
||||
"userName" typed StringType,
|
||||
"appName" typed StringType,
|
||||
"developerEmail" typed StringType
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -152,14 +155,17 @@ class elasticsearchMetrics extends elasticsearch {
|
||||
}
|
||||
}
|
||||
|
||||
def indexMetric(userId: String, url: String, date: Date) {
|
||||
def indexMetric(userId: String, url: String, date: Date, userName: String, appName: String, developerEmail: String) {
|
||||
if (Props.getBool("allow_elasticsearch", false) && Props.getBool("allow_elasticsearch_metrics", false) ) {
|
||||
try {
|
||||
client.execute {
|
||||
index into esIndex / "request" fields (
|
||||
"userId" -> userId,
|
||||
"url" -> url,
|
||||
"date" -> date
|
||||
"date" -> date,
|
||||
"userName" -> userName,
|
||||
"appName" -> appName,
|
||||
"developerEmail" -> developerEmail
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ Berlin 13359, Germany
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-get_api_key">
|
||||
<div class="green-button">
|
||||
<a href="/consumer-registration">Get your API key</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -176,7 +176,7 @@ Berlin 13359, Germany
|
||||
</div>
|
||||
|
||||
|
||||
<div id="main-apiexplorer">
|
||||
<div id="main-apiexplorer" class="green-button">
|
||||
<a class="api-explorer-link" data-lift="WebUI.apiExplorerLink" href="">Go to API Explorer</a>
|
||||
</div>
|
||||
|
||||
@ -187,50 +187,50 @@ Berlin 13359, Germany
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-DirectLogin-Python"><img src="https://static.openbankproject.com/images/sandbox/showcases/python.png" width="156" height="156" alt="python" /></a>
|
||||
<h2>Python</h2>
|
||||
By OpenBankProject
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-DirectLogin-Python">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Django"><img src="https://static.openbankproject.com/images/sandbox/showcases/django.png" width="156" height="156" alt="django" /></a>
|
||||
<h2>Django</h2>
|
||||
By Azd325
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Django">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Node"><img src="https://static.openbankproject.com/images/sandbox/showcases/nodejs.png" width="156" height="156" alt="nodejs" /></a>
|
||||
<h2>NodeJS</h2>
|
||||
By OpenBankProject
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Node">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Mac"><img src="https://static.openbankproject.com/images/sandbox/showcases/mac.png" width="156" height="156" alt="mac"/></a>
|
||||
<h2>Mac</h2>
|
||||
By T0rst
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Mac">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-IOS"><img src="https://static.openbankproject.com/images/sandbox/showcases/ios.png" width="156" height="156" alt="ios" /></a>
|
||||
<h2>IOS</h2>
|
||||
By T0rst
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-IOS">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Android"><img src="https://static.openbankproject.com/images/sandbox/showcases/android.png" width="156" height="156" alt="android" /></a>
|
||||
<h2>Android</h2>
|
||||
By OpenBankProject
|
||||
By <a href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Android">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/OpenBankProject/Social-Finance"><img src="https://static.openbankproject.com/images/sandbox/showcases/scala.png" width="156" height="156" alt="scala" /></a>
|
||||
<h2>Scala (liftweb)</h2>
|
||||
By OpenBankProject
|
||||
By <a href="https://github.com/OpenBankProject/Social-Finance">OpenBankProject</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="https://github.com/solonas/OBP-PHP-HelloWorld"><img src="https://static.openbankproject.com/images/sandbox/showcases/php.png" width="156" height="156" alt="php" /></a>
|
||||
<h2>PHP</h2>
|
||||
By Solonas
|
||||
By <a href="https://github.com/solonas/OBP-PHP-HelloWorld">Solonas</a>
|
||||
</div>
|
||||
<div class="main-showcases-item">
|
||||
<a href="http://obp.sckhoo.com"><img src="https://static.openbankproject.com/images/sandbox/showcases/csharp.png" width="156" height="156" alt="csharp" /></a>
|
||||
<h2>C#</h2>
|
||||
By Sweechem<br /><span class="small">(this has a nice OAuth walk through with C# snippets)</span>
|
||||
By <a href="http://obp.sckhoo.com">Sweechem</a>
|
||||
</div>
|
||||
|
||||
<p>Please make sure you are using the correct sandbox domain in all calls when using the SDKs. In doubt, drop us a line.</p>
|
||||
<p>Please make sure you are using the correct sandbox domain when using the SDKs. In doubt, drop us a line.</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -312,7 +312,7 @@ Berlin 13359, Germany
|
||||
|
||||
<div id="main-start_building">
|
||||
<h1>Get started building your application using this sandbox now, register for a developer key</h1>
|
||||
<div class="main-get_api_key">
|
||||
<div class="green-button">
|
||||
<a href="/consumer-registration">Get your API key</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#authorizeSection,
|
||||
#registerAppSection,
|
||||
#create-sandbox-account,
|
||||
#header-decoration {
|
||||
#header-decoration,
|
||||
#main-showcases,
|
||||
#main-support {
|
||||
background-color: #ff6a10 !important;
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
@import url('reset.css');
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
|
||||
body, div, h1, h2, h3, h4, p, span {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #666666;
|
||||
text-decoration: none;
|
||||
@ -211,7 +212,10 @@ input.submit {
|
||||
}
|
||||
|
||||
#main-about p.about-text a {
|
||||
color: lightgray;
|
||||
color: white;
|
||||
}
|
||||
#main-about p.about-text a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#main-links {
|
||||
@ -228,6 +232,7 @@ input.submit {
|
||||
border: 2px solid gray;
|
||||
border-radius: 25px;
|
||||
margin: 30px 20px 0px 20px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#main-sandbox {
|
||||
@ -239,7 +244,7 @@ input.submit {
|
||||
|
||||
#main-sandbox h1 {
|
||||
color: white;
|
||||
font-size: 35px;
|
||||
font-size: 36px;
|
||||
margin-bottom: 15px;
|
||||
margin-left: -5px;
|
||||
margin-top: 15px;
|
||||
@ -248,7 +253,7 @@ input.submit {
|
||||
|
||||
#main-get_started {
|
||||
text-align: left;
|
||||
margin: 40px auto;
|
||||
margin: 50px auto;
|
||||
background-color: #f1f4f8;
|
||||
}
|
||||
|
||||
@ -286,7 +291,7 @@ input.submit {
|
||||
#main-get_started h1 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 35px;
|
||||
font-size: 36px;
|
||||
margin: 30px 0 30px 0;
|
||||
display: table-caption;
|
||||
}
|
||||
@ -297,13 +302,14 @@ input.submit {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.main-get_api_key {
|
||||
padding: 30px 0;
|
||||
#main-get_started .green-button {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
|
||||
#main-apis {
|
||||
text-align: left;
|
||||
margin: 40px auto;
|
||||
margin: 50px auto;
|
||||
display: table;
|
||||
}
|
||||
.main-apis-row {
|
||||
@ -330,8 +336,8 @@ input.submit {
|
||||
#main-apis h1 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 35px;
|
||||
margin: 30px 0 30px 0;
|
||||
font-size: 36px;
|
||||
margin: 0px 0 30px 0;
|
||||
display: table-caption;
|
||||
}
|
||||
#main-apis h2 {
|
||||
@ -340,19 +346,6 @@ input.submit {
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
#main-apiexplorer, .main-get_api_key {
|
||||
width: 300px;
|
||||
margin: 30px auto;
|
||||
text-align: center;
|
||||
}
|
||||
#main-apiexplorer a, .main-get_api_key a {
|
||||
border: 3px solid green;
|
||||
border-radius: 25px;
|
||||
color: green;
|
||||
padding: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
#main-showcases {
|
||||
margin: 50px auto;
|
||||
@ -370,24 +363,24 @@ input.submit {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#main-showcases p {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.main-showcases-item {
|
||||
display: inline-block;
|
||||
margin: 30px 10px;
|
||||
width: 230px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.main-showcases-item span {
|
||||
font-size: small;
|
||||
.main-showcases-item a {
|
||||
color: white;
|
||||
}
|
||||
.main-showcases-item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
#main-faq {
|
||||
text-align: left;
|
||||
margin: 40px auto;
|
||||
margin: 50px auto;
|
||||
padding:0 40px;
|
||||
background-color: #f1f4f8;
|
||||
}
|
||||
@ -401,7 +394,7 @@ input.submit {
|
||||
}
|
||||
|
||||
#main-faq h2 {
|
||||
font-size: 18px;
|
||||
font-size: 18px; /* text won't fit on line with 24px */
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
@ -447,8 +440,8 @@ input.submit {
|
||||
|
||||
|
||||
#main-start_building {
|
||||
margin: 40px 0;
|
||||
padding: 40px 80px;
|
||||
margin: 50px 0;
|
||||
padding: 0 80px;
|
||||
}
|
||||
#main-start_building h1 {
|
||||
font-size: 36px;
|
||||
@ -458,7 +451,7 @@ input.submit {
|
||||
|
||||
|
||||
#main-partners {
|
||||
margin: 40px auto 0 auto;
|
||||
margin: 50px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -499,6 +492,21 @@ input.submit {
|
||||
|
||||
|
||||
|
||||
.green-button {
|
||||
width: 300px;
|
||||
margin: 30px auto;
|
||||
text-align: center;
|
||||
}
|
||||
.green-button a {
|
||||
border: 3px solid green;
|
||||
border-radius: 25px;
|
||||
color: green;
|
||||
padding: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -630,7 +638,6 @@ input.submit {
|
||||
width: inherit;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.account-in-content form.login .field .forgot {
|
||||
@ -921,6 +928,9 @@ span.alias_indicator_private {
|
||||
#footer a {
|
||||
color: white;
|
||||
}
|
||||
#footer #copyright {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
#authorizeSection {
|
||||
|
||||
@ -114,9 +114,8 @@ Berlin 13359, Germany
|
||||
The main content gets bound here
|
||||
</section>
|
||||
<footer id="footer">
|
||||
<div>
|
||||
<a href="http://openbankproject.com">Open Bank Project</a> is ©2011-2016 <a href="http://tesobe.com">TESOBE</a> and distributed under the AGPL and commercial licenses.</p>
|
||||
<br/>
|
||||
<div id="copyright">
|
||||
<a href="http://openbankproject.com">Open Bank Project</a> is ©2011-2016 <a href="http://tesobe.com">TESOBE</a> and distributed under the AGPL and commercial licenses.
|
||||
</div>
|
||||
<div>
|
||||
<a href="http://twitter.com/#!/OpenBankProject">Twitter</a> |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user