<!--
Google IO 2012 HTML5 Slide Template

Authors: Eric Bidelman <ebidel@gmail.com>
         Luke Mahé <lukem@google.com>

URL: https://code.google.com/p/io-2012-slides
-->
<!DOCTYPE html>
<html>
<head>
    <title>Google IO 2012</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    <!--<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">-->
    <!--<meta name="viewport" content="width=device-width, initial-scale=1.0">-->
    <!--This one seems to work all the time, but really small on ipad-->
    <!--<meta name="viewport" content="initial-scale=0.4">-->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <link rel="stylesheet" media="all" href="theme/css/default.css">
    <link rel="stylesheet" media="only screen and (max-device-width: 480px)" href="theme/css/phone.css">
    <base target="_blank">
    <!-- This amazingness opens all links in a new tab. -->
    <script data-main="js/slides" src="js/require-1.0.8.min.js"></script>
</head>
<body style="opacity: 0">

<slides class="layout-widescreen">

    <slide class="title-slide segue nobackground">
        <aside class="gdbar"><img src="images/fg_icon_128.png"></aside>
        <!-- The content of this hgroup is replaced programmatically through the slide_config.json. -->
        <hgroup class="auto-fadein">
            <h1 data-config-title><!-- populated from slide_config.json --></h1>

            <h2 data-config-subtitle><!-- populated from slide_config.json --></h2>

            <p data-config-presenter><!-- populated from slide_config.json --></p>
        </hgroup>
    </slide>

    <slide>
        <hgroup>
            <h2>What is authorization</h2>
        </hgroup>
        <article>
            <q>
                Authorization is the function of specifying access rights to resources, which is related to information
                security
                and computer security in general and to access control in particular. More formally, <i>to authorize</i>
                is to<br/>
                <b>define access policy</b>.
            </q>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>The question is ...</h2>
        </hgroup>
        <article class="flexbox vcenter centered">
            <img src="images/thinking-cap.gif" height="75%" width="75%" alt="Description" title="Description">
            <p>HOW TO DEFINE POLICY THE EASIEST and RELIABLE WAY</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>The imperative way</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            public void updateOrganization(Organization organization) {
                <b>GastroUser principal = SecurityContext.getInstance().getPrincipal();
                if (principal != null &&
                    (principal.isAdministrator() || principal.isOrganizationOwner(organization.getId()))) {</b>
                    //check preconditions
                    Assert.isTrue(
                        organization.isApproved(),
                        "Cannot update organization that hasn't been yet approved!"
                    );
                    //do update logic
                    updateOrganizationBySystem(organization);
                    //inform the system about the action
                    publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
                <b>else {
                    throw new AccessDeniedException("User is not authorized to execute this action!");
                }</b>
            }
            </pre>
            <p>Six out of 16 lines to each method to execute authorization?</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>Let's refactor this ...</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>/** This is shared **/
            public static void authorizeAdministrator(Organization organization) {
                GastroUser principal = SecurityContext.getInstance().getPrincipal();
                if (!(principal != null &&
                    (principal.isAdministrator() || principal.isOrganizationOwner(organization.getId())))) {
                    throw new AccessDeniedException("User is not authorized to execute this action!");
                }
            }</b>

            public void updateOrganization(Organization organization) {
                <b>authorizeAdministrator(organization);</b>
                Assert.isTrue(
                    organization.isApproved(),
                    "Cannot update organization that was not yet approved!"
                );
                updateOrganizationBySystem(organization);
                publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
            }
            </pre>
            <p>Well now we have single line but we are forced to use only static methods and singletons to keep it pretty.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>The declarative way - AOP</h2>
            <h3>Isn't it a sample example of crosscutting concern?</h3>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>@Pointcut("execution(* com.fg.whatever.business..*.update*(..))")</b>
            public static void authorizeAdministrator(<b>Organization organization!!!</b>) {
                GastroUser principal = SecurityContext.getInstance().getPrincipal();
                if (!(principal != null &&
                    (principal.isAdministrator() || principal.isOrganizationOwner(organization.getId())))) {
                    throw new AccessDeniedException("User is not authorized to execute this action!");
                }
            }

            public void updateOrganization(Organization organization) {
                Assert.isTrue(
                    organization.isApproved(),
                    "Cannot update organization that was not yet approved!"
                );
                updateOrganizationBySystem(organization);
                publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
            }
            </pre>
            <p>We have to think about unified naming schemes and with complex authorization this solution quickly runs of breath.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>How does Spring solve it?</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>@PreAuthorize(
                "principal.userObject != null and " +
                "(principal.userObject.administrator or principal.userObject.isOwnerOf(#organization.id))"
            )</b>
            public void updateOrganization(Organization organization) {
                Assert.isTrue(
                    organization.isApproved(),
                    "Cannot update organization that was not yet approved!"
                );
                updateOrganizationBySystem(organization);
                publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
            }
            </pre>
            <p>Half way there - even if this is currently recommended and standard solution.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>Let's refactor expression to share the logic ...</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>private static final String IS_ADMINISTRATOR = "...";
            private static final String IS_ORGANIZATION_OWNER = "...";

            @PreAuthorize(IS_ADMINISTRATOR  + " or " + IS_ORGANIZATION_OWNER)</b>
            public void updateOrganization(Organization organization) {
                Assert.isTrue(
                    organization.isApproved(),
                    "Cannot update organization that was not yet approved!"
                );
                updateOrganizationBySystem(organization);
                publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
            }
            </pre>
            <p>Better - but we cannot look up for all methods that are protected to admin access.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>Let's refactor expression to share the logic ...</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>@AllowedForAdministrator
            @AllowedForEdeeAdmin
            @AllowedForOrganizationOwner
            @DeniedForMerchant</b>
            public void updateOrganization(Organization organization) {
                Assert.isTrue(
                    organization.isApproved(),
                    "Cannot update organization that was not yet approved!"
                );
                updateOrganizationBySystem(organization);
                publisher.publishEvent(new OrganizationUpdatedEvent(this, organization));
            }
            </pre>
            <p>This is something I use now in my projects.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>How do I test authorization logic?</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>@Test(expected = AccessDeniedException.class)
            public void anonymousUserShouldBeDeniedToUpdateOrganization() throws Exception {</b>
                Organization company = organizationManager.getOrganizationById(2);
                organizationManager.approveOrganization(company);
            <b>}

            @Test
            @RunAsUser("soltysova@fg.cz")
            public void administratorShouldBeAbleToUpdateOrganization() throws Exception {</b>
                final Organization company = organizationManager.getOrganizationById(2);
                organizationManager.updateOrganization(company);
            <b>}</b>
            </pre>
            <p>I test the logic alongside with authorization aspect - but I can separate it if necessary.</p>
        </article>
    </slide>

    <slide>
        <hgroup>
            <h2>Sometimes we need to change user contexts</h2>
        </hgroup>
        <article class="smaller">
            <pre class="prettyprint" data-lang="java">
            <b>@Test
            @RunAsUser("cizek@fg.cz")
            public void administratorCanCancelBonusActionOfTheUser() throws Exception {</b>
                <b>//in the anonymous class we are logged as soltysova@fg - user
                RunAsSupportTestExecutionListener.executeInContextOf(
                    "soltysova@fg.cz", applicationContext,
                    new RunAsSupportTestExecutionListener.ExecutionClosure() {
                        @Override
                        public void execute() {
                            Bonus inPreparationBonus = bonusManager.getBonusById(20);
                            bonusManager.publishBonus(inPreparationBonus, Locale.ENGLISH);
                        }
                    }
                );

                //in this context we are cizek@fg.cz - administrator</b>
                Bonus bonusToCancel = bonusManager.getBonusById(20);
                bonusManager.cancelBonus(bonusToCancel, "I am cancelling the action.", true);
                assertEquals(ProductState.CANCELED, bonusManager.getBonusById(20).getState());
            <b>}</b>
            </pre>
            <p>Inside single test I could switch user contexts as I wish. Closures would help here though.</p>
        </article>
    </slide>

    <slide class="thank-you-slide dark nobackground">
        <aside class="gdbar right"><img src="images/fg_icon_128.png"></aside>
        <article class="flexbox vleft auto-fadein">
            <h2>... and how do YOU fight with authorization?</h2>
            <p>Thank you for sharing!</p>
        </article>
        <p class="auto-fadein" data-config-contact>
            <!-- populated from slide_config.json -->
        </p>
    </slide>

    <slide class="backdrop"></slide>

</slides>

<script>
    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
    _gaq.push(['_trackPageview']);

    (function () {
        var ga = document.createElement('script');
        ga.type = 'text/javascript';
        ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ga, s);
    })();
</script>

<!--[if IE]>
<script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>CFInstall.check({mode: 'overlay'});</script>
<![endif]-->
</body>
</html>