Skip to content

Commit 42c98df

Browse files
authored
Merge pull request #991 from hmrc/BDOG-2908
BDOG-2908 add request new vpn button for team admins
2 parents 9da0b47 + be31fa3 commit 42c98df

File tree

5 files changed

+169
-45
lines changed

5 files changed

+169
-45
lines changed

app/uk/gov/hmrc/cataloguefrontend/connector/UserManagementConnector.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,12 @@ class UserManagementConnector @Inject()(
102102
.flatMap:
103103
case Right(res) => Future.successful(res)
104104
case Left(err) => Future.failed(RuntimeException(s"Request to $url failed with upstream error: ${err.message}"))
105+
106+
def requestNewVpnCert(username: UserName)(using HeaderCarrier): Future[Option[String]] =
107+
val url: URL = url"$baseUrl/user-management/users/$username/vpn"
108+
httpClientV2
109+
.post(url)
110+
.execute[Either[UpstreamErrorResponse, JsValue]]
111+
.flatMap:
112+
case Right(json) => Future.successful((json \ "ticket_number").validate[String].asOpt)
113+
case Left(err) => Future.failed(RuntimeException(s"Request to $url failed with upstream error: ${err.message}"))

app/uk/gov/hmrc/cataloguefrontend/users/UsersController.scala

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,23 @@ import uk.gov.hmrc.cataloguefrontend.auth.CatalogueAuthBuilders
2323
import uk.gov.hmrc.cataloguefrontend.config.UserManagementPortalConfig
2424
import uk.gov.hmrc.cataloguefrontend.connector.UserManagementConnector
2525
import uk.gov.hmrc.cataloguefrontend.model.{TeamName, UserName}
26-
import uk.gov.hmrc.cataloguefrontend.users.view.html.{UserInfoPage, UserListPage, UserSearchResults}
26+
import uk.gov.hmrc.cataloguefrontend.users.view.html.{UserInfoPage, UserListPage, UserSearchResults, VpnRequestSentPage}
2727
import uk.gov.hmrc.cataloguefrontend.view.html.error_404_template
2828
import uk.gov.hmrc.http.UpstreamErrorResponse
2929
import uk.gov.hmrc.internalauth.client.{FrontendAuthComponents, IAAction, Resource, ResourceType, Retrieval}
3030
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
3131

3232
import javax.inject.{Inject, Singleton}
3333
import scala.concurrent.{ExecutionContext, Future}
34+
import scala.util.control.NonFatal
3435

3536
@Singleton
3637
class UsersController @Inject()(
3738
userManagementConnector: UserManagementConnector
3839
, userInfoPage : UserInfoPage
3940
, userListPage : UserListPage
4041
, userSearchResults : UserSearchResults
42+
, vpnRequestSentPage : VpnRequestSentPage
4143
, umpConfig : UserManagementPortalConfig
4244
, override val mcc : MessagesControllerComponents
4345
, override val auth : FrontendAuthComponents
@@ -80,6 +82,31 @@ class UsersController @Inject()(
8082
val teams = retrieval.fold(Set.empty[TeamName])(_.map(_.resourceLocation.value.stripPrefix("teams/")).map(TeamName.apply))
8183
teams.contains(TeamName("*")) || teams.exists(user.teamNames.contains) // Global admin or admin for user's team
8284

85+
val requestNewVpnCert: Action[AnyContent] =
86+
BasicAuthAction.async: request =>
87+
given RequestHeader = request
88+
auth.verify(Retrieval.locations(
89+
resourceType = Some(ResourceType("catalogue-frontend")),
90+
action = Some(IAAction("CREATE_USER"))
91+
)).flatMap: retrieval =>
92+
request.body.asFormUrlEncoded.flatMap(_.get("username").flatMap(_.headOption)).map(UserName.apply)
93+
.fold(
94+
Future.failed(RuntimeException("Could not request a new vpn certificate as hidden username form field was empty"))
95+
): username =>
96+
userManagementConnector.getUser(username).flatMap:
97+
case Some(user) =>
98+
if(isAdminForUser(retrieval, user)) then
99+
userManagementConnector.requestNewVpnCert(username)
100+
.map(ticketOpt => Created(vpnRequestSentPage(user, ticketOpt)))
101+
.recover:
102+
case NonFatal(e) =>
103+
logger.error(s"Error requesting new VPN certificate: ${e.getMessage}", e)
104+
Redirect(routes.UsersController.user(username)).flashing("error" -> "Error requesting VPN Certificate. Contact #team-platops")
105+
else
106+
Future.successful(Redirect(routes.UsersController.user(username)).flashing("error" -> "Permission denied"))
107+
case _ =>
108+
Future.successful(Redirect(routes.UsersController.user(username)).flashing("error" -> "Unable to find user"))
109+
83110
val users: Action[AnyContent] =
84111
BasicAuthAction.async: request =>
85112
given RequestHeader = request

app/uk/gov/hmrc/cataloguefrontend/users/view/UserInfoPage.scala.html

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@
3131
)
3232

3333
@standard_layout(user.username.asString, active = "users") {
34+
@request.flash.get("error").map { msg =>
35+
<div class="alert alert-danger alert-dismissible fade show mt-2" role="alert">
36+
@msg
37+
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
38+
</div>
39+
}
40+
3441
<h1 class="page-heading mt-4">@user.username.asString</h1>
3542

3643
<section class="section-wrapper">
@@ -124,60 +131,93 @@ <h1 class="page-heading mt-4">@user.username.asString</h1>
124131
<div class="row mb-3">
125132
<div class="col-md-4">
126133
<div class="card mb-4">
127-
<div class="card-header">
134+
<div class="card-header d-flex justify-content-between align-items-center">
128135
<div class="h4 mb-0">Tooling</div>
129-
</div>
130-
131-
<div class="card-body">
132136
@if(canCreateUsers) {
133-
<div class="btn-group float-end" role="group">
137+
<div>
134138
<a class="btn btn-success" href="@userRoutes.EditUserController.editUserLanding(user.username, user.organisation)">Edit</a>
135139
</div>
136140
}
137-
<ul class="list-unstyled">
138-
<li>
139-
<span class="glyphicon glyphicon-@{
140-
if(userAccess.vpn) "ok text-success" else "minus text-secondary"
141-
} me-2"></span>
142-
<strong>VPN</strong>
143-
</li>
144-
<li>
145-
<span class="glyphicon glyphicon-@{
146-
if(userAccess.jira) "ok text-success" else "minus text-secondary"
147-
} me-2"></span>
148-
<strong>Jira</strong>
149-
</li>
150-
<li>
151-
<span class="glyphicon glyphicon-@{
152-
if(userAccess.confluence) "ok text-success" else "minus text-secondary"
153-
} me-2"></span>
154-
<strong>Confluence</strong>
155-
</li>
156-
<li>
157-
<span class="glyphicon glyphicon-@{
158-
if(userAccess.devTools) "ok text-success" else "minus text-secondary"
159-
} me-2"></span>
160-
<strong>Developer Tools</strong> <i>(Kibana, Grafana, Jenkins)</i>
161-
</li>
162-
<li>
163-
<span class="glyphicon glyphicon-@{
164-
if(userAccess.googleApps) "ok text-success" else "minus text-secondary"
165-
} me-2"></span>
166-
<strong>Google Workspace</strong> <i>(Gmail, Calendar, Hangouts, Drive etc)</i>
167-
</li>
168-
<li>
169-
<abbr style="text-decoration: none;
170-
border-bottom: none;" title="Unable to determine if BitWarden access has been granted">
171-
<span class="glyphicon glyphicon-question-sign me-2"></span>
172-
</abbr>
173-
<strong>Bitwarden</strong>
174-
</li>
175-
</ul>
176141
</div>
142+
143+
<div class="card-body">
144+
<ul class="list-unstyled">
145+
<li>
146+
<span class="glyphicon glyphicon-@{
147+
if(userAccess.vpn) "ok text-success" else "minus text-secondary"
148+
} me-2"></span>
149+
<strong>VPN</strong>
150+
</li>
151+
<li>
152+
<span class="glyphicon glyphicon-@{
153+
if(userAccess.jira) "ok text-success" else "minus text-secondary"
154+
} me-2"></span>
155+
<strong>Jira</strong>
156+
</li>
157+
<li>
158+
<span class="glyphicon glyphicon-@{
159+
if(userAccess.confluence) "ok text-success" else "minus text-secondary"
160+
} me-2"></span>
161+
<strong>Confluence</strong>
162+
</li>
163+
<li>
164+
<span class="glyphicon glyphicon-@{
165+
if(userAccess.devTools) "ok text-success" else "minus text-secondary"
166+
} me-2"></span>
167+
<strong>Developer Tools</strong> <i>(Kibana, Grafana, Jenkins)</i>
168+
</li>
169+
<li>
170+
<span class="glyphicon glyphicon-@{
171+
if(userAccess.googleApps) "ok text-success" else "minus text-secondary"
172+
} me-2"></span>
173+
<strong>Google Workspace</strong> <i>(Gmail, Calendar, Hangouts, Drive etc)</i>
174+
</li>
175+
<li>
176+
<abbr style="text-decoration: none;
177+
border-bottom: none;" title="Unable to determine if BitWarden access has been granted">
178+
<span class="glyphicon glyphicon-question-sign me-2"></span>
179+
</abbr>
180+
<strong>Bitwarden</strong>
181+
</li>
182+
</ul>
183+
</div>
184+
@if(canCreateUsers) {
185+
<div class="card-footer">
186+
<div class="d-flex justify-content-between">
187+
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#vpnModal">
188+
Request VPN Certificate
189+
</button>
190+
</div>
191+
</div>
192+
}
177193
</div>
178194
</div>
179195
</div>
180196
}
181197
}
182198
</section>
199+
200+
<div class="modal fade" id="vpnModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="vpnModalTitle" aria-hidden="true">
201+
<div class="modal-dialog">
202+
<div class="modal-content">
203+
<div class="modal-header">
204+
<h5 class="modal-title" id="vpnModalTitle">Request New VPN Certificate</h5>
205+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
206+
</div>
207+
<div class="modal-body">
208+
<p>You are about to request a new VPN certificate for @user.username.asString.</p>
209+
<p>If you continue you will invalidate @{user.username.asString}'s existing certificate.</p>
210+
<p>To proceed click 'Submit' and a new certificate will be sent to @user.primaryEmail shortly.</p>
211+
</div>
212+
<div class="modal-footer">
213+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
214+
<form id="vpn-form" method="post" action="@userRoutes.UsersController.requestNewVpnCert">
215+
@csrfFormField
216+
<input name="username" type="hidden" value="@user.username.asString">
217+
<button id="vpn-submit" class="btn btn-success" type="submit">Submit</button>
218+
</form>
219+
</div>
220+
</div>
221+
</div>
222+
</div>
183223
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@*
2+
* Copyright 2023 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+
@import uk.gov.hmrc.cataloguefrontend.users.{routes => usersRoutes}
18+
@import uk.gov.hmrc.cataloguefrontend.users.User
19+
20+
@this()
21+
22+
@(user : User,
23+
ticketOpt: Option[String]
24+
)(implicit
25+
request : RequestHeader
26+
)
27+
28+
@standard_layout("Request a new VPN certificate", active = "users") {
29+
30+
<section class="section-wrapper">
31+
<h1 class="page-heading mt-4">Request Sent</h1>
32+
33+
<p>You have requested that a new VPN certificate be created for @user.username.asString and sent to @user.primaryEmail</p>
34+
35+
@ticketOpt match {
36+
case Some(ticket) => {
37+
<p>To review the progression of this request and check the users details are correct, please view the <a target="_blank" href="https://jira.tools.tax.service.gov.uk/browse/@ticket">@ticket</a> Jira ticket.</p>
38+
}
39+
case _ => {
40+
<p>To review the progression of this request and check the users details are correct, please view the <a target="_blank" href="https://jira.tools.tax.service.gov.uk/projects/ACRS/issues/ACRS">ACRS Jira Board</a>.</p>
41+
}
42+
}
43+
44+
<a href="@usersRoutes.UsersController.users" class="btn btn-secondary" role="button">Back to Users Page</a>
45+
46+
</section>
47+
}

conf/app.routes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ GET /vulnerabilities/timeline uk.gov.hmrc.cataloguefr
136136
GET /users uk.gov.hmrc.cataloguefrontend.users.UsersController.users
137137
GET /users-search uk.gov.hmrc.cataloguefrontend.users.UsersController.userSearch(query: String)
138138
GET /users/:username uk.gov.hmrc.cataloguefrontend.users.UsersController.user(username: UserName)
139+
POST /users/new-vpn-cert uk.gov.hmrc.cataloguefrontend.users.UsersController.requestNewVpnCert
139140

140141
GET /users/edit-user/access uk.gov.hmrc.cataloguefrontend.users.EditUserController.editUserLanding(username: UserName, organisation: Option[String] ?= None)
141142
POST /users/edit-user/access uk.gov.hmrc.cataloguefrontend.users.EditUserController.editUserAccess(username: UserName, organisation: Option[String] ?= None)

0 commit comments

Comments
 (0)