Skip to content

Commit 1f194c0

Browse files
committed
Merge pull request #5 from hmrc/API-1346
API-1346 ability to revoke an application authority
2 parents ae3d6e8 + 669c19f commit 1f194c0

28 files changed

+490
-70
lines changed

.travis.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
21
sudo: false
32
language: scala
43
scala:
5-
- 2.11.6
4+
- 2.11.7
65
jdk:
76
- oraclejdk8
87
cache:
98
directories:
109
- '$HOME/.ivy2/cache'
11-
branches:
12-
except:
13-
- master
14-
10+
notifications:
11+
email: false
12+
13+
before_script:
14+
- "export DISPLAY=:99.0"
15+
- "sh -e /etc/init.d/xvfb start"
16+
- sleep 3 # give xvfb some time to start
17+
18+
script:
19+
- sbt ++$TRAVIS_SCALA_VERSION test acceptance:test

app/config/frontendAppConfig.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object FrontendAppConfig extends AppConfig with ServicesConfig {
3535

3636
private val caFrontendHost = configuration.getString("ca-frontend.host").getOrElse("")
3737
private val contactHost = configuration.getString("contact-frontend.host").getOrElse("")
38-
private val apiRevocationFrontendHost = configuration.getString("api-revocation-frontend.host").getOrElse("")
38+
private val loginCallbackBaseUrl = getConfString("auth.login-callback.base-url", "")
3939

4040
private val contactFormServiceIdentifier = "api-revocation-frontend"
4141

@@ -44,6 +44,6 @@ object FrontendAppConfig extends AppConfig with ServicesConfig {
4444
override lazy val analyticsHost = loadConfig(s"google-analytics.host")
4545
override lazy val reportAProblemPartialUrl = s"$contactHost/contact/problem_reports_ajax?service=$contactFormServiceIdentifier"
4646
override lazy val reportAProblemNonJSUrl = s"$contactHost/contact/problem_reports_nonjs?service=$contactFormServiceIdentifier"
47-
override lazy val signInUrl = s"$caFrontendHost/gg/sign-in?continue=$apiRevocationFrontendHost/api-revocation/applications"
47+
override lazy val signInUrl = s"$caFrontendHost/gg/sign-in?continue=$loginCallbackBaseUrl/applications-permissions-withdrawal/applications"
4848
override lazy val signOutUrl = s"$caFrontendHost/gg/sign-out"
4949
}

app/connectors/DelegatedAuthorityConnector.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,31 @@
1616

1717
package connectors
1818

19+
import java.util.UUID
20+
1921
import config.WSHttp
2022
import models.AppAuthorisation
2123
import uk.gov.hmrc.play.config.ServicesConfig
22-
import uk.gov.hmrc.play.http.{HeaderCarrier, HttpGet, HttpPost}
24+
import uk.gov.hmrc.play.http.{HeaderCarrier, HttpDelete, HttpGet, HttpPost}
25+
import scala.concurrent.ExecutionContext.Implicits.global
2326

2427
trait DelegatedAuthorityConnector {
2528

2629
val delegatedAuthorityUrl: String
27-
val http: HttpPost with HttpGet
30+
val http: HttpPost with HttpGet with HttpDelete
2831

2932
def fetchApplicationAuthorities()(implicit hc: HeaderCarrier) = {
30-
http.GET[Seq[AppAuthorisation]](s"$delegatedAuthorityUrl/authority/granted-applications")
33+
val url = s"$delegatedAuthorityUrl/authority/granted-applications"
34+
http.GET[Seq[AppAuthorisation]](url) recover {
35+
recovery(url)
36+
}
37+
}
38+
39+
def revokeApplicationAuthority(applicationId: UUID)(implicit hc: HeaderCarrier) = {
40+
val url = s"$delegatedAuthorityUrl/authority/granted-application/$applicationId"
41+
http.DELETE(url) recover {
42+
recovery(url)
43+
}
3144
}
3245
}
3346

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2016 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package connectors
18+
19+
import java.util.UUID
20+
21+
import config.WSHttp
22+
import connectors.DelegatedAuthorityConnector._
23+
import models.ApplicationDetails
24+
import uk.gov.hmrc.play.http._
25+
26+
import scala.concurrent.Future
27+
import scala.concurrent.ExecutionContext.Implicits.global
28+
29+
trait ThirdPartyApplicationConnector {
30+
31+
val applicationBaseUrl: String
32+
33+
val http: HttpPost with HttpGet
34+
35+
def fetchApplication(applicationId: UUID)(implicit hc: HeaderCarrier): Future[ApplicationDetails] = {
36+
val url = s"$applicationBaseUrl/application/$applicationId"
37+
http.GET[ApplicationDetails](url) recover {
38+
recovery(url)
39+
}
40+
}
41+
}
42+
43+
object ThirdPartyApplicationConnector extends ThirdPartyApplicationConnector {
44+
override val applicationBaseUrl: String = s"${baseUrl("third-party-application")}"
45+
override val http = WSHttp
46+
}

app/connectors/package.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2016 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package object connectors {
18+
19+
private[connectors] def recovery(url: String): PartialFunction[Throwable, Nothing] = {
20+
case e: Throwable => throw DownstreamMicroserviceException(url, e)
21+
}
22+
23+
case class DownstreamMicroserviceException(url: String, t: Throwable) extends RuntimeException(s"Error obtained while calling: [$url]", t)
24+
}

app/controllers/Revocation.scala

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,21 @@
1616

1717
package controllers
1818

19-
import connectors.DelegatedAuthorityConnector
19+
import java.util.UUID
20+
21+
import config.FrontendAuthConnector
22+
import connectors.{DelegatedAuthorityConnector, ThirdPartyApplicationConnector}
2023
import play.api.mvc.Action
2124
import uk.gov.hmrc.play.frontend.auth.connectors.AuthConnector
2225
import uk.gov.hmrc.play.frontend.controller.FrontendController
23-
import config.FrontendAuthConnector
2426

2527
import scala.concurrent.Future
2628

2729
trait Revocation extends FrontendController with Authentication {
2830

2931
val authConnector: AuthConnector
3032
val delegatedAuthorityConnector: DelegatedAuthorityConnector
33+
val thirdPartyApplicationConnector: ThirdPartyApplicationConnector
3134

3235
val start = Action.async { implicit request =>
3336
Future.successful(Ok(views.html.revocation.start()))
@@ -37,9 +40,27 @@ trait Revocation extends FrontendController with Authentication {
3740
delegatedAuthorityConnector.fetchApplicationAuthorities()
3841
.map(applications => Ok(views.html.revocation.authorizedApplications(applications)))
3942
}
43+
44+
def withdrawPage(id: UUID) = authenticated.async { implicit user => implicit request =>
45+
thirdPartyApplicationConnector.fetchApplication(id)
46+
.map(application => Ok(views.html.revocation.withdrawPermission(application)))
47+
}
48+
49+
def withdrawAction(id: UUID) = authenticated.async { implicit user => implicit request =>
50+
delegatedAuthorityConnector.revokeApplicationAuthority(id).map { _ =>
51+
Redirect(routes.Revocation.withdrawConfirmationPage(id))
52+
}
53+
}
54+
55+
def withdrawConfirmationPage(id: UUID) = authenticated.async { implicit user => implicit request =>
56+
thirdPartyApplicationConnector.fetchApplication(id).map { app =>
57+
Ok(views.html.revocation.permissionWithdrawn(app))
58+
}
59+
}
4060
}
4161

4262
object Revocation extends Revocation {
4363
override val authConnector = FrontendAuthConnector
4464
override val delegatedAuthorityConnector = DelegatedAuthorityConnector
65+
override val thirdPartyApplicationConnector = ThirdPartyApplicationConnector
4566
}

app/models/Authority.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package models
1818

19+
import java.util.UUID
20+
1921
import org.joda.time.DateTime
2022
import play.api.libs.json.Json
2123

@@ -25,7 +27,7 @@ object Scope {
2527
implicit val format = Json.format[Scope]
2628
}
2729

28-
case class ThirdPartyApplication(id: String, name: String)
30+
case class ThirdPartyApplication(id: UUID, name: String)
2931

3032
object ThirdPartyApplication {
3133
implicit val format = Json.format[ThirdPartyApplication]
@@ -38,3 +40,9 @@ case class AppAuthorisation(application: ThirdPartyApplication,
3840
object AppAuthorisation {
3941
implicit val format = Json.format[AppAuthorisation]
4042
}
43+
44+
case class ApplicationDetails(id: UUID, name: String)
45+
46+
object ApplicationDetails {
47+
implicit val format = Json.format[ApplicationDetails]
48+
}

app/views/govuk_wrapper.scala.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
@insideHeader = {
3131
@uiLayouts.header_nav(
32-
navTitle = Some("Data permissions withdrawal"),
32+
navTitle = Some("Applications permissions withdrawal"),
3333
navTitleLink = None,
3434
showBetaLink = false,
3535
navLinks = Some(headerNavLinks))

app/views/revocation/authorizedApplications.scala.html

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,28 @@ <h1>Authorised software applications</h1>
1616

1717
@defining(DateTimeFormat.forPattern("dd MMMM yyyy")) { dateFormatter =>
1818
<ul>
19-
@for(application <- applications) {
19+
@for(app <- applications) {
2020
<li>
21-
<details data-@{application.application.id}>
22-
<summary data-name-@{application.application.id}>
23-
@{application.application.name}
21+
<details data-@{app.application.id}>
22+
<summary data-name-@{app.application.id}>
23+
@{app.application.name}
2424
</summary>
2525
<div class="panel-indent">
2626
<p>
2727
<strong>This application can perform the following actions in relation to HMRC data:</strong>
2828
</p>
2929
<ul class="bullets">
30-
@for(scope <- application.scopes) {
31-
<li data-scope-@{application.application.id}='@{scope.key}'>
30+
@for(scope <- app.scopes) {
31+
<li data-scope-@{app.application.id}='@{scope.key}'>
3232
@{scope.description}
3333
</li>
3434
}
3535
</ul>
36-
<p data-grant-date-@{application.application.id}>
37-
<strong>Permission granted on:</strong> @{dateFormatter.print(application.earliestGrantDate)}
36+
<p data-grant-date-@{app.application.id}>
37+
<strong>Permission granted on:</strong> @{dateFormatter.print(app.earliestGrantDate)}
3838
</p>
39-
<form action="">
40-
<input type="submit" class="button" value="Withdraw permission"/>
41-
</form>
39+
40+
<a data-withdraw-permission-@{app.application.id} href="@routes.Revocation.withdrawPage(app.application.id)" class="button">Withdraw permission</a>
4241
</div>
4342
</details>
4443
</li>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@(application: ApplicationDetails)(implicit request: Request[_])
2+
3+
@body = {
4+
<div class="content__body">
5+
<header>
6+
<h1>Permission withdrawn</h1>
7+
</header>
8+
9+
<p data-withdrawn-message>
10+
<strong>@{application.name}</strong> can no longer access HMRC data.
11+
</p>
12+
<p>
13+
<a data-applications-link href="@controllers.routes.Revocation.listAuthorizedApplications">
14+
Continue to your authorised software applications.
15+
</a>
16+
</p>
17+
</div>
18+
}
19+
20+
@main_template(title = "Permission withdrawn", bodyClasses = None) {
21+
@body
22+
}

0 commit comments

Comments
 (0)