users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] Re: Working example app demonstrating identity store usage

From: Werner Keil <werner.keil_at_gmail.com>
Date: Mon, 7 Dec 2015 21:00:58 +0100

Compared to one or the other JSR also supposed to be on the EE 8 release
train, there are a few slightly more advanced, but also some that show very
little activity, even on mailing lists. When it comes to that I think 375
isn't doing so badly, but it could improve;-)

Kind Regards,
Werner

On Mon, Dec 7, 2015 at 6:46 PM, arjan tijms <arjan.tijms_at_gmail.com> wrote:

> Hi,
>
> On Mon, Dec 7, 2015 at 5:44 PM, Werner Keil <werner.keil_at_gmail.com> wrote:
>
>> Well the Renewal Ballot passed about 2 weeks ago:
>> https://jcp.org/en/jsr/results?id=5832
>> Not sure, if Twitter was eligable or not to vote (they also recently got
>> a new EC rep) but everyone else voted in favor.
>>
>
> That's good to hear, thanks. I totally missed that event somehow.
>
>
>
>> a JSR like this would at least be at great risk to miss the Java EE 8
>> train (at least the Process JSRs some have the 3rd Renewal Ballot now;-)
>>
>
> Definitely. I'm slightly worried though about the amount of progress we
> made till this far and the activity of the EG in general. I know I am
> responsible for that myself too, as I took over a month before starting on
> the extended functionality I just presented, but still. Really hope we can
> make somewhat more progress in the new year.
>
> Kind regards,
> Arjan Tijms
>
>
>
>
>
>
>>
>> Regards,
>> Werner
>>
>> On Mon, Dec 7, 2015 at 5:35 PM, arjan tijms <arjan.tijms_at_gmail.com>
>> wrote:
>>
>>> Hi,
>>>
>>> On Mon, Dec 7, 2015 at 5:31 PM, Werner Keil <werner.keil_at_gmail.com>
>>> wrote:
>>>
>>>> I guess whatever I present next week as a live demo, I'll highlight,
>>>> that it's still very much "in motion"
>>>>
>>>
>>> Sounds like a good idea.
>>>
>>>
>>>> but as soon (if I recall it right, there's up to another 6 maybe 9
>>>> months to produce an EDR after the Renewal Ballot passed;-) as EDR 1 is
>>>> out, those structures should probably take a more stable shape, even if
>>>> some packages could well be added later.
>>>>
>>>
>>> Any word about the renewal ballot or EDR?
>>>
>>> Is there a template for the EDR spec document btw? I could perhaps try
>>> to write a draft EDR, so that we at least have something. Or is there a
>>> draft EDR document already?
>>>
>>> Kind regards,
>>> Arjan Tijms
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>>
>>>> Kind Regards,
>>>> Werner
>>>>
>>>>
>>>>
>>>> On Mon, Dec 7, 2015 at 5:27 PM, arjan tijms <arjan.tijms_at_gmail.com>
>>>> wrote:
>>>>
>>>>> True, so that's likely the step after the mechanism.
>>>>>
>>>>> The package for whatever authorization things we do could be
>>>>> javax.security.authorization, but then -the- authorization API sits a
>>>>> package below that.
>>>>>
>>>>> JACC is an issue by itself too. There is some immense power lurking
>>>>> inside it, but just as with the JASPIC situation a few years back has many
>>>>> implementation and TCK issues.
>>>>>
>>>>> The biggest problem of JACC (IMHO) is that the only way to code
>>>>> against it is by installing a jar in a very obscure server specific way.
>>>>> There are two environment parameters specified for this where you put 2
>>>>> classes that are somehow on the classpath of the server. But, in modern
>>>>> servers there's hardly a notion of -the- classpath.
>>>>>
>>>>> In practice, it's nearly impossible to use JACC with some servers. I
>>>>> asked e.g. JBoss what the proprietary method is to install a JACC provider
>>>>> on their server, and it doesn't seem anyone readily knows this.
>>>>>
>>>>> If an MR for JACC could only introduce a factory API like JASPIC has,
>>>>> to programmatically install a JACC provider from within a war, then this EG
>>>>> could put some higher level functionality on top of it.
>>>>>
>>>>> Kind regards,
>>>>> Arjan Tijms
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Mon, Dec 7, 2015 at 5:16 PM, Werner Keil <werner.keil_at_gmail.com>
>>>>> wrote:
>>>>>
>>>>>> Yep, and at the moment most "authorization" elements are under the
>>>>>> somewhat cryptical
>>>>>> javax.security.jacc
>>>>>>
>>>>>> "Java Authorization Contract for Containers API"
>>>>>>
>>>>>> Werner
>>>>>>
>>>>>> On Mon, Dec 7, 2015 at 5:03 PM, arjan tijms <arjan.tijms_at_gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> On Mon, Dec 7, 2015 at 4:42 PM, Werner Keil <werner.keil_at_gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> P.s.: I'm not sure, if the API would stick to a rather lengthy
>>>>>>>> package name like: javax.security.authenticationmechanism ?;-)
>>>>>>>>
>>>>>>>> Werner
>>>>>>>>
>>>>>>>
>>>>>>> I hear you, this package name and specifically the classes inside
>>>>>>> it, have not been given much thought yet, but since they were needed to get
>>>>>>> the sample to work I had to put them somewhere.
>>>>>>>
>>>>>>> In general when we are going to discuss this, there are some issues.
>>>>>>>
>>>>>>> There is already an existing package javax.security.auth.message,
>>>>>>> which is owned by JASPIC, so we won't touch it. But I was wondering about
>>>>>>> just javax.security.auth, which is the prefix package. I guess we could
>>>>>>> touch that (create new sub-packages of that), but this is a bit of an issue
>>>>>>> too since we more or less agreed here that just "auth" is not desirable. It
>>>>>>> can mean either "authentication" or "authorization".
>>>>>>>
>>>>>>> But if we create a package javax.security.authentication next to the
>>>>>>> existing javax.security.auth, where both mean "authentication", it kinda
>>>>>>> looks messy as well.
>>>>>>>
>>>>>>> Nevertheless, with a javax.security.authentication, we could put the
>>>>>>> mechanism classes in javax.security.authentication.mechanism (but in total
>>>>>>> this is actually 1 char longer). The alternative could be
>>>>>>> javax.security.auth.mechanism, but yeah, troublesome...
>>>>>>>
>>>>>>> Kind regards,
>>>>>>> Arjan Tijms
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Dec 7, 2015 at 10:45 AM, Werner Keil <werner.keil_at_gmail.com
>>>>>>>> > wrote:
>>>>>>>>
>>>>>>>>> Thanks, I hope to have a closer look at it during the week or
>>>>>>>>> weekend. Would be nice to show something like it in Tel Aviv in 10 days ;-)
>>>>>>>>>
>>>>>>>>> Kind Regards,
>>>>>>>>>
>>>>>>>>> Werner Keil | JCP Executive Committee Member, JSR 363 Co Spec
>>>>>>>>> Lead | Eclipse UOMo Lead, Babel Language Champion | Apache
>>>>>>>>> Committer
>>>>>>>>>
>>>>>>>>> Twitter @wernerkeil | @UnitAPI | @JSR354 | @AgoravaProj | @DeviceMap
>>>>>>>>> | #DevOps | #EclipseUOMo
>>>>>>>>> Skype werner.keil | Google+ gplus.to/wernerkeil
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Werner Keil
>>>>>>>>> [image: https://]about.me/wernerkeil
>>>>>>>>> <https://about.me/wernerkeil?promo=email_sig>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Dec 7, 2015 at 10:37 AM, arjan tijms <
>>>>>>>>> arjan.tijms_at_gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> I have extended the example a little. To keep the original
>>>>>>>>>> example as-is and as small/simple as possible, I did the extended work in a
>>>>>>>>>> new repo here: https://github.com/arjantijms/mechanism-to-store-x
>>>>>>>>>>
>>>>>>>>>> This adds:
>>>>>>>>>>
>>>>>>>>>> * Working implementation and example of
>>>>>>>>>> @DataBaseIdentityStoreDefinition
>>>>>>>>>> * Example of custom (app provided) identity store.
>>>>>>>>>>
>>>>>>>>>> See:
>>>>>>>>>>
>>>>>>>>>> *
>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/app-db/src/main/java/test/Servlet.java
>>>>>>>>>> *
>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store-x/blob/master/app-custom/src/main/java/test/TestIdentityStore.java
>>>>>>>>>>
>>>>>>>>>> I also fixed the "mechanism-to-store-app-1.0-SNAPSHOT" name. The
>>>>>>>>>> example now uses simple names for the war and therefor the URL:
>>>>>>>>>> localhost:8080/app-db, localhost:8080/app-mem, localhost:8080/app-custom.
>>>>>>>>>>
>>>>>>>>>> The examples were tested on GlassFish (4.1.1) and JBoss (WildFly
>>>>>>>>>> 10rc4). Note that JBoss needs the proprietary JASPIC activation before this
>>>>>>>>>> works (still hoping Darran can do something here to lift this).
>>>>>>>>>>
>>>>>>>>>> In order to work around a GlassFish bug (see
>>>>>>>>>> https://java.net/jira/browse/GLASSFISH-21447) I had to put the
>>>>>>>>>> data source in java:global. Due to another GlassFish bug where GF doesn't
>>>>>>>>>> unbind the data source when the application is undeployed you actually have
>>>>>>>>>> to stop and start GlassFish before the deploying the app again.
>>>>>>>>>>
>>>>>>>>>> Kind regards,
>>>>>>>>>> Arjan Tijms
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Sun, Oct 25, 2015 at 5:55 AM, Alex Kosowski <
>>>>>>>>>> alex.kosowski_at_oracle.com> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Arjan,
>>>>>>>>>>>
>>>>>>>>>>> That example app is terrific! I would like to demonstrate it
>>>>>>>>>>> during the JavaOne JSR 375 BOF. The app does not look like much, but when
>>>>>>>>>>> you realize the caller was authenticated using data from an annotation, you
>>>>>>>>>>> realize how these simple standardizations will make a BIG impact.
>>>>>>>>>>>
>>>>>>>>>>> The only issue I ran into when deploying on GlassFish 4.1 was
>>>>>>>>>>> the default app name was " mechanism-to-store-app-1.0-SNAPSHOT", which made
>>>>>>>>>>> the context root " /mechanism-to-store-app-1.0-SNAPSHOT". But I changed the
>>>>>>>>>>> context root to "/mechanism-to-store-app" in the GF admin console and the
>>>>>>>>>>> example works as you described.
>>>>>>>>>>>
>>>>>>>>>>> Thanks again!
>>>>>>>>>>> Alex
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 10/24/15 6:19 PM, Alex Kosowski wrote:
>>>>>>>>>>>
>>>>>>>>>>> Thanks Arjan,
>>>>>>>>>>>
>>>>>>>>>>> Perhaps you would provide an example of using the
>>>>>>>>>>> @CredentialCapable(UsernamePasswordCredential.class) qualifier? Also,
>>>>>>>>>>> perhaps you would have an example of extending a standard identity store
>>>>>>>>>>> with a custom defined one to support a custom defined credential?
>>>>>>>>>>>
>>>>>>>>>>> Just some suggestions.
>>>>>>>>>>>
>>>>>>>>>>> With regards,
>>>>>>>>>>> Alex
>>>>>>>>>>>
>>>>>>>>>>> On 10/19/15 2:53 PM, arjan tijms wrote:
>>>>>>>>>>>
>>>>>>>>>>> Hi,
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Oct 19, 2015 at 7:46 PM, Alex Kosowski <
>>>>>>>>>>> alex.kosowski_at_oracle.com> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Thanks Arjan! I cannot wait to try it!
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Looking forward to hearing feedback, thanks.
>>>>>>>>>>>
>>>>>>>>>>> I'll try later this week to create another project that
>>>>>>>>>>> implements some more identity stores and also has an example for a user
>>>>>>>>>>> defined one.
>>>>>>>>>>>
>>>>>>>>>>> Kind regards,
>>>>>>>>>>> Arjan Tijms
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On 10/19/15 1:27 PM, arjan tijms wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>
>>>>>>>>>>>>> I've created a working zero config demo application that shows
>>>>>>>>>>>>> how an authentication mechanism can use an identity store.
>>>>>>>>>>>>>
>>>>>>>>>>>>> It's located here:
>>>>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store
>>>>>>>>>>>>>
>>>>>>>>>>>>> It uses a selection of the types Alex proposed, with the
>>>>>>>>>>>>> adjustments as discussed. This demo app uses one of the two ways to set an
>>>>>>>>>>>>> Identity Store; "declaratively for a standard provided store" as proposed
>>>>>>>>>>>>> by Reza in issue
>>>>>>>>>>>>> https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-9 (the
>>>>>>>>>>>>> other option which is not demoed here is a store fully defined by the
>>>>>>>>>>>>> application).
>>>>>>>>>>>>>
>>>>>>>>>>>>> The store is declared using an annotation corresponding to
>>>>>>>>>>>>> option 3 in the list presented earlier. I put this annotation on a test
>>>>>>>>>>>>> Servlet (but it can be put anywhere):
>>>>>>>>>>>>>
>>>>>>>>>>>>> @EmbeddedIdentityStoreDefinition({
>>>>>>>>>>>>> @Credentials(callerName = "reza", password = "secret1",
>>>>>>>>>>>>> groups = { "foo", "bar" }),
>>>>>>>>>>>>> @Credentials(callerName = "alex", password = "secret2",
>>>>>>>>>>>>> groups = { "foo", "kaz" }),
>>>>>>>>>>>>> @Credentials(callerName = "arjan", password = "secret3",
>>>>>>>>>>>>> groups = { "foo" }) })
>>>>>>>>>>>>> @DeclareRoles({ "foo", "bar", "kaz" })
>>>>>>>>>>>>> @WebServlet("/servlet")
>>>>>>>>>>>>> public class Servlet extends HttpServlet
>>>>>>>>>>>>>
>>>>>>>>>>>>> See:
>>>>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store/blob/master/app/src/main/java/test/Servlet.java
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> This annotation is picked up by a CDI extension and a Bean<T>
>>>>>>>>>>>>> is created for it:
>>>>>>>>>>>>>
>>>>>>>>>>>>> public <T> void processBean(@Observes ProcessBean<T>
>>>>>>>>>>>>> eventIn, BeanManager beanManager) {
>>>>>>>>>>>>>
>>>>>>>>>>>>> ProcessBean<T> event = eventIn; // JDK8 u60 workaround
>>>>>>>>>>>>>
>>>>>>>>>>>>> Optional<EmbeddedIdentityStoreDefinition> result =
>>>>>>>>>>>>> getAnnotation(beanManager, event.getAnnotated(),
>>>>>>>>>>>>> EmbeddedIdentityStoreDefinition.class);
>>>>>>>>>>>>> if (result.isPresent()) {
>>>>>>>>>>>>> identityStoreBean = new
>>>>>>>>>>>>> CdiProducer<IdentityStore>()
>>>>>>>>>>>>> .scope(ApplicationScoped.class)
>>>>>>>>>>>>> .types(IdentityStore.class)
>>>>>>>>>>>>> .create(e -> new
>>>>>>>>>>>>> EmbeddedIdentityStore(result.get().value()));
>>>>>>>>>>>>>
>>>>>>>>>>>>> }
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> This Bean<T> is subsequently registered with the container:
>>>>>>>>>>>>>
>>>>>>>>>>>>> public void afterBean(final @Observes AfterBeanDiscovery
>>>>>>>>>>>>> afterBeanDiscovery) {
>>>>>>>>>>>>> if (identityStoreBean != null) {
>>>>>>>>>>>>> afterBeanDiscovery.addBean(identityStoreBean);
>>>>>>>>>>>>> }
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> See:
>>>>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store/blob/master/jsr375/src/main/java/org/glassfish/jsr375/cdi/CdiExtension.java
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> The Identity Store implementation is vendor specific. It's not
>>>>>>>>>>>>> in the javax.security API package. This way vendors can optimise the
>>>>>>>>>>>>> implementation and/or map it to their existing artefacts.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The sample implementation maps the data in the annotation to a
>>>>>>>>>>>>> Map:
>>>>>>>>>>>>>
>>>>>>>>>>>>> private Map<String, Credentials> callerToCredentials;
>>>>>>>>>>>>>
>>>>>>>>>>>>> public EmbeddedIdentityStore(Credentials[] credentials) {
>>>>>>>>>>>>> callerToCredentials =
>>>>>>>>>>>>> stream(credentials).collect(toMap(
>>>>>>>>>>>>> e -> e.callerName(),
>>>>>>>>>>>>> e -> e)
>>>>>>>>>>>>> );
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> And in the validate() method it simply checks if the
>>>>>>>>>>>>> credentials for the requested caller name are present:
>>>>>>>>>>>>>
>>>>>>>>>>>>> public CredentialValidationResult
>>>>>>>>>>>>> validate(UsernamePasswordCredential usernamePasswordCredential) {
>>>>>>>>>>>>> Credentials credentials =
>>>>>>>>>>>>> callerToCredentials.get(usernamePasswordCredential.getCaller());
>>>>>>>>>>>>>
>>>>>>>>>>>>> if (credentials != null &&
>>>>>>>>>>>>> usernamePasswordCredential.getPassword().compareTo(credentials.password()))
>>>>>>>>>>>>> {
>>>>>>>>>>>>> return new CredentialValidationResult(
>>>>>>>>>>>>> VALID,
>>>>>>>>>>>>> credentials.callerName(),
>>>>>>>>>>>>> asList(credentials.groups())
>>>>>>>>>>>>> );
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> return INVALID_RESULT;
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> See:
>>>>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store/blob/master/jsr375/src/main/java/org/glassfish/jsr375/identitystores/EmbeddedIdentityStore.java
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> The application uses a very basic SAM. This one uses the plain
>>>>>>>>>>>>> non-simplified JASPIC API. The SAM obtains the identity store via CDI and
>>>>>>>>>>>>> then utilises it to perform the "credential to identity data" function:
>>>>>>>>>>>>>
>>>>>>>>>>>>> String name = request.getParameter("name");
>>>>>>>>>>>>> Password password = new
>>>>>>>>>>>>> Password(request.getParameter("password"));
>>>>>>>>>>>>>
>>>>>>>>>>>>> // Obtain a reference to the Identity Store
>>>>>>>>>>>>> IdentityStore identityStore =
>>>>>>>>>>>>> CDI.current().select(IdentityStore.class).get();
>>>>>>>>>>>>>
>>>>>>>>>>>>> // Delegate the {credentials in -> identity data out}
>>>>>>>>>>>>> function to
>>>>>>>>>>>>> // the Identity Store
>>>>>>>>>>>>> CredentialValidationResult result =
>>>>>>>>>>>>> identityStore.validate(new UsernamePasswordCredential(name, password));
>>>>>>>>>>>>>
>>>>>>>>>>>>> if (result.getStatus() == VALID) {
>>>>>>>>>>>>> callbacks = new Callback[] {
>>>>>>>>>>>>> // The name of the authenticated caller
>>>>>>>>>>>>> new CallerPrincipalCallback(clientSubject,
>>>>>>>>>>>>> result.getCallerName()),
>>>>>>>>>>>>> // the groups of the authenticated caller (for test
>>>>>>>>>>>>> // assume non-null, non-empty)
>>>>>>>>>>>>> new GroupPrincipalCallback(clientSubject,
>>>>>>>>>>>>> result.getCallerGroups().toArray(new String[0])) };
>>>>>>>>>>>>> } else {
>>>>>>>>>>>>> throw new AuthException("Login failed");
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> See:
>>>>>>>>>>>>> https://github.com/arjantijms/mechanism-to-store/blob/master/app/src/main/java/test/TestServerAuthModule.java
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Finally the demo app uses a simple (non-protected) Servlet
>>>>>>>>>>>>> (the one shown above) that prints out the details of the authenticated
>>>>>>>>>>>>> user. If the application is deployed to a stock GlassFish without any
>>>>>>>>>>>>> configuration whatsoever being done it can be requested via:
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> http://localhost:8080/mechanism-to-store-app/servlet?name=reza&password=secret1
>>>>>>>>>>>>> <
>>>>>>>>>>>>> http://localhost:8080/mechanism-to-store-app/servlet?name=reza&password=secret1
>>>>>>>>>>>>> >
>>>>>>>>>>>>>
>>>>>>>>>>>>> If all went well this prints out the following:
>>>>>>>>>>>>>
>>>>>>>>>>>>> This is a servlet
>>>>>>>>>>>>> web username: reza
>>>>>>>>>>>>> web user has role "foo": true
>>>>>>>>>>>>> web user has role "bar": true
>>>>>>>>>>>>> web user has role "kaz": false
>>>>>>>>>>>>>
>>>>>>>>>>>>> Needless to say here that this is just for demo'ing one of the
>>>>>>>>>>>>> smallest possible SAMs that interact with the caller. Putting the password
>>>>>>>>>>>>> in the URL is of course not suited for any real live usage.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Note that this particular demo only demonstrates a few of the
>>>>>>>>>>>>> discussed options. I also made a few practical choices here and there to be
>>>>>>>>>>>>> able to implement the application which can of course be discussed further.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thoughts?
>>>>>>>>>>>>>
>>>>>>>>>>>>> Kind regards,
>>>>>>>>>>>>> Arjan Tijms
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>