Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import scala.annotation.nowarn
*/
sealed trait AgentApplication:

def _id: AgentApplicationId
def internalUserId: InternalUserId
def linkId: LinkId
def groupId: GroupId
Expand All @@ -51,7 +52,7 @@ sealed trait AgentApplication:
// case llp: ApplicationLlp => llp.copy(applicationState = nextApplicationState)

/* derived stuff: */

val agentApplicationId: AgentApplicationId = _id
val lastUpdated: Instant = Instant.now(Clock.systemUTC())

val hasFinished: Boolean =
Expand Down Expand Up @@ -84,6 +85,7 @@ sealed trait AgentApplication:
/** Sole Trader Application. This case class represents the data entered by a user for registering as a sole trader.
*/
final case class AgentApplicationSoleTrader(
override val _id: AgentApplicationId,
override val internalUserId: InternalUserId,
override val linkId: LinkId,
override val groupId: GroupId,
Expand All @@ -103,6 +105,7 @@ extends AgentApplication:
/** Application Applicatoin for Limited Liability Partnership (Llp). This case class represents the data entered by a user for registering as an Llp.
*/
final case class AgentApplicationLlp(
override val _id: AgentApplicationId,
override val internalUserId: InternalUserId,
override val linkId: LinkId,
override val groupId: GroupId,
Expand Down
34 changes: 34 additions & 0 deletions app/uk/gov/hmrc/agentregistration/shared/AgentApplicationId.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2025 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.agentregistration.shared

import org.bson.types.ObjectId
import play.api.libs.json.Format
import uk.gov.hmrc.agentregistration.shared.util.JsonFormatsFactory

import javax.inject.Singleton

/** Agent application Identifier, which is unique for an application
*/
final case class AgentApplicationId(value: String)

object AgentApplicationId:
given format: Format[AgentApplicationId] = JsonFormatsFactory.makeValueClassFormat

@Singleton
class AgentApplicationIdGenerator:
def nextApplicationId(): AgentApplicationId = AgentApplicationId(ObjectId.get().toHexString)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2025 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.agentregistration.shared.llp

import play.api.libs.json.*
import uk.gov.hmrc.agentregistration.shared.AgentApplicationId
import uk.gov.hmrc.agentregistration.shared.InternalUserId
import uk.gov.hmrc.agentregistration.shared.llp.ProvidedDetailsState.Finished
import uk.gov.hmrc.agentregistration.shared.util.SafeEquals.===

import java.time.Instant

/** Member provided details for Limited Liability Partnership (Llp). This case class represents the data entered by a user for approving as an Llp.
*/
final case class MemberProvidedDetails(
_id: MemberProvidedDetailsId,
internalUserId: InternalUserId,
createdAt: Instant,
providedDetailsState: ProvidedDetailsState,
applicationId: AgentApplicationId
):

val memberProvidedDetailsId: MemberProvidedDetailsId = _id
val hasFinished: Boolean = if providedDetailsState === Finished then true else false
val isInProgress: Boolean = !hasFinished

object MemberProvidedDetails:
given format: OFormat[MemberProvidedDetails] = Json.format[MemberProvidedDetails]
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2025 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.agentregistration.shared.llp

import org.bson.types.ObjectId
import play.api.libs.json.Format
import uk.gov.hmrc.agentregistration.shared.util.JsonFormatsFactory

import javax.inject.Singleton

/** Member provided details Identifier, which is unique for an member provided details
*/
final case class MemberProvidedDetailsId(value: String)

object MemberProvidedDetailsId:
given format: Format[MemberProvidedDetailsId] = JsonFormatsFactory.makeValueClassFormat

@Singleton
class MemberProvidedDetailsIdGenerator:
def nextMemberProvidedDetailsId(): MemberProvidedDetailsId = MemberProvidedDetailsId(ObjectId.get().toHexString)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2025 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.agentregistration.shared.llp

import play.api.libs.json.Format
import uk.gov.hmrc.agentregistration.shared.util.JsonFormatsFactory

enum ProvidedDetailsState:

case Started
case Finished

object ProvidedDetailsState:
given Format[ProvidedDetailsState] = JsonFormatsFactory.makeEnumFormat[ProvidedDetailsState]
43 changes: 42 additions & 1 deletion app/uk/gov/hmrc/agentregistrationfrontend/action/Actions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import play.api.mvc.*
import play.api.mvc.Results.BadRequest
import play.api.mvc.Results.Redirect
import play.twirl.api.HtmlFormat
import uk.gov.hmrc.agentregistrationfrontend.action.providedetails.IndividualAuthorisedAction
import uk.gov.hmrc.agentregistrationfrontend.action.providedetails.IndividualAuthorisedRequest
import uk.gov.hmrc.agentregistrationfrontend.action.providedetails.llp.MemberProvideDetailsRequest
import uk.gov.hmrc.agentregistrationfrontend.action.providedetails.llp.ProvideDetailsAction
import uk.gov.hmrc.agentregistrationfrontend.controllers.AppRoutes
import uk.gov.hmrc.agentregistrationfrontend.util.RequestAwareLogging
import uk.gov.hmrc.agentregistrationfrontend.forms.helpers.SubmissionHelper
Expand All @@ -35,7 +39,9 @@ import scala.util.chaining.scalaUtilChainingOps
class Actions @Inject() (
actionBuilder: DefaultActionBuilder,
authorisedAction: AuthorisedAction,
agentApplicationAction: AgentApplicationAction
agentApplicationAction: AgentApplicationAction,
individualAuthorisedAction: IndividualAuthorisedAction,
provideDetailsAction: ProvideDetailsAction
)(using ExecutionContext)
extends RequestAwareLogging:

Expand Down Expand Up @@ -116,3 +122,38 @@ extends RequestAwareLogging:
if SubmissionHelper.getSubmitAction(request).isSaveAndComeBackLater then Redirect(AppRoutes.apply.SaveForLaterController.show)
else originalResult
)

val authorisedIndividual: ActionBuilder[IndividualAuthorisedRequest, AnyContent] = action
.andThen(individualAuthorisedAction)

val getProvideDetailsInProgress: ActionBuilder[MemberProvideDetailsRequest, AnyContent] = authorisedIndividual
.andThen(provideDetailsAction)
.ensure(
condition = _.memberProvidedDetails.isInProgress,
resultWhenConditionNotMet =
implicit request =>
// TODO: TBC - where to redirect
val call = AppRoutes.apply.AgentApplicationController.genericExitPage
logger.warn(
s"The provided details are in the final state" +
s" (current provided details: ${request.memberProvidedDetails.providedDetailsState.toString}), " +
s"redirecting to [${call.url}]."
)
Redirect(call.url)
)

val getSubmitedDetailsInProgress: ActionBuilder[MemberProvideDetailsRequest, AnyContent] = authorisedIndividual
.andThen(provideDetailsAction)
.ensure(
condition = _.memberProvidedDetails.hasFinished,
resultWhenConditionNotMet =
implicit request =>
// TODO: TBC - where to redirect
val call = AppRoutes.apply.AgentApplicationController.genericExitPage
logger.warn(
s"The provided details are in the final state" +
s" (current provided details: ${request.memberProvidedDetails.providedDetailsState.toString}), " +
s"redirecting to [${call.url}]."
)
Redirect(call.url)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2023 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.agentregistrationfrontend.action.providedetails

import com.google.inject.Inject
import com.google.inject.Singleton
import play.api.mvc.*
import play.api.mvc.Results.*
import sttp.model.Uri.UriContext
import uk.gov.hmrc.agentregistration.shared.*
import uk.gov.hmrc.agentregistrationfrontend.action.FormValue
import uk.gov.hmrc.agentregistrationfrontend.action.MergeFormValue
import uk.gov.hmrc.agentregistrationfrontend.config.AppConfig
import uk.gov.hmrc.agentregistrationfrontend.util.Errors
import uk.gov.hmrc.agentregistrationfrontend.util.RequestAwareLogging
import uk.gov.hmrc.agentregistrationfrontend.util.RequestSupport.hc
import uk.gov.hmrc.agentregistrationfrontend.views.ErrorResults
import uk.gov.hmrc.auth.core.*
import uk.gov.hmrc.auth.core.AuthProvider.GovernmentGateway
import uk.gov.hmrc.auth.core.retrieve.*
import uk.gov.hmrc.auth.core.retrieve.v2.Retrievals

import scala.concurrent.ExecutionContext
import scala.concurrent.Future

class IndividualAuthorisedRequest[A](
val internalUserId: InternalUserId,
val request: Request[A],
val credentials: Credentials
)
extends WrappedRequest[A](request)

object IndividualAuthorisedRequest:

given [T, A]: MergeFormValue[IndividualAuthorisedRequest[A], T] =
(
r: IndividualAuthorisedRequest[A],
t: T
) =>
new IndividualAuthorisedRequest[A](
r.internalUserId,
r.request,
r.credentials
) with FormValue[T]:
val formValue: T = t

@Singleton
class IndividualAuthorisedAction @Inject() (
af: AuthorisedFunctions,
errorResults: ErrorResults,
appConfig: AppConfig,
cc: MessagesControllerComponents
)
extends ActionRefiner[Request, IndividualAuthorisedRequest]
with RequestAwareLogging:

override protected def executionContext: ExecutionContext = cc.executionContext

override protected def refine[A](request: Request[A]): Future[Either[Result, IndividualAuthorisedRequest[A]]] =
given r: Request[A] = request

af.authorised(
AuthProviders(GovernmentGateway)
and AffinityGroup.Individual
).retrieve(
Retrievals.allEnrolments
and Retrievals.internalId
and Retrievals.credentials
).apply:
case allEnrolments ~ maybeInternalId ~ credentials =>
Future.successful(Right(new IndividualAuthorisedRequest(
internalUserId = maybeInternalId
.map(InternalUserId.apply)
.getOrElse(Errors.throwServerErrorException("Retrievals for internalId is missing")),
request = request,
credentials = credentials.getOrElse(Errors.throwServerErrorException("Retrievals for credentials is missing"))
)))
.recoverWith:
case _: NoActiveSession =>
logger.info(s"Unauthorised because of 'NoActiveSession', redirecting to sign in page")
Future.successful(Left(Redirect(
url = appConfig.signInUri(uri"""${appConfig.thisFrontendBaseUrl + request.uri}""").toString()
)))
case e: UnsupportedAuthProvider =>
logger.info(s"Unauthorised because of '${e.reason}', ${e.toString}")
Future.successful(Left(
errorResults.unauthorised(
message = e.reason
)
))
case e: UnsupportedAffinityGroup =>
logger.info(s"Unauthorised because of '${e.reason}', ${e.toString}")
Future.successful(Left(
errorResults.unauthorised(
message = e.reason
)
))
case e: AuthorisationException =>
logger.info(s"Unauthorised because of '${e.reason}', ${e.toString}")
Future.successful(Left(
errorResults.unauthorised(
message = e.toString
)
))

private given ExecutionContext = cc.executionContext
Loading