0
votes

I just changed from a json datastore for my single app Polymer web app to polymerfire. All data in my web app is public, and only one admin should change data, so I have just one top node for all page data.

For login, I use the firebase-auth element with a public provider, and have set the rules to give my admin write privileges.

But now I am struggling around how to give the logged in admin a feedback to show he is logged in. For that, I have added a second top node in my database ('isAdmin'), that is only readible for a logged in admin, with a key of 'Admin' and a Boolean value of 'true'. I have tried to bind to that with firebase-document and use it in a dom-if template like so:

    <firebase-auth 
        id="auth" 
        user="{{user}}"
        provider="google"
        app-name="myApp"
        signed-in="{{signedIn}}">
    </firebase-auth>        
    <firebase-document
        log
        id="adminCheck"
        app-name="myApp"
        path="/isAdmin"
        data="{{firebaseAdmin}}">
    </firebase-document>

    <paper-button raised on-tap="login" hidden="[[user]]">Log in</paper-button>        
    <paper-button raised on-tap="logout" hidden="[[!user]]">Log out</paper-button>

    <template is="dom-if" if="[[firebaseAdmin.Admin]]" restamp>
        <span>Hello [[user.displayName]]!</span>
    </template>

However, as only a logged in admin can read the node, the admin value actually never changes in my javascript, only if I refresh the browser after login. So if I try to log the state like so:

<script>
    Polymer({
        is: 'my-login',
        properties: {
            user: {
                type: Object
            },
            firebaseAdmin: {
                type: Object
            },
        },
        _checkAdmin: function() {
            var myVal = this.$.adminCheck.getStoredValue('/isAdmin');
            console.log('Value for admin stored in firebase-document element: ' + myVal.Admin);
            console.log('Polymer property for admin: ' + this.firebaseAdmin.Admin);

        },
        login: function() {
            this.$.auth.signInWithPopup();
            this._checkAdmin();
        },
        logout: function() {            
            this.$.auth.signOut();
            this._checkAdmin();
        }
    });
</script>

I'd always get:

Value for admin stored in firebase-document element: undefined Polymer property for admin: undefined

back from my _checkAdmin function, even if logged in. Only when I refresh the page after admin login, I'd get a log from firebase-document with the stored object at the isAdmin node:

Got stored value! Object {Admin: true}

, and now I'd be stuck with this value, even if logged out. My _checkAdmin function would then always log this:

Value for admin stored in firebase-document element: undefined
Polymer property for admin: true

I then tried to various things unsuccessfully, but now I guess this is not the appropriate way to check for a logged in admin? I just don't think I should check uid's in javascript directly.

Glad if any pro would be around and wanting to help me out, Alex

Thanks to Michael Bleigh's help I finally got it working, like so:

<dom-module id="my-login">
<template>
    <style>
        span {
            margin-left: 1rem;
        }
        paper-button {
            background: -webkit-linear-gradient(#fff, #333 66%);
            -webkit-background-clip: text;
             background-clip: text;
            -webkit-text-fill-color: transparent;
        }
    </style>        
    <firebase-auth 
        id="auth" 
        user="{{user}}"
        provider="google"
        app-name="myApp"
        signed-in="{{signedIn}}">
    </firebase-auth>        
    <firebase-document
        app-name="myApp"
        path="/admins/[[user.uid]]"
        data="{{isAdmin}}">
    </firebase-document>

    <paper-button raised on-tap="login" hidden="[[user]]">Log in</paper-button>        
    <paper-button raised on-tap="logout" hidden="[[!user]]">Log out</paper-button>        
    <template is="dom-if" if="[[admin]]" restamp>
        <span hidden="[[!user]]">Hello [[user.displayName]]!</span>
    </template>


</template>
<script>
    Polymer({
        is: 'my-login',
        properties: {
            user: {
                type: Object
            },
            isAdmin: {
                type: Object
            },
            admin: {
                type: Boolean,
                computed: '_checkAdmin(isAdmin)'
            }
        },            
        _checkAdmin: function(isAdmin) {
            if (typeof isAdmin === 'boolean') {
                console.log('Polymer property for admin is defined');
                return true;
            }
        },
        login: function() {
            this.$.auth.signInWithPopup();

        },
        logout: function() {            
            this.$.auth.signOut();
        }
    });
</script>

1

1 Answers

2
votes

A typical way to solve this is by writing admins as a boolean map in the Realtime Database:

- admins
  - {uid}: true
  - {uid}: true

You would then set up security rules like:

{
  "admins": {
    "$uid": {
      ".read": "auth.uid === $uid"
    }
  }
}

This allows each user to read their own admins node to check whether or not they're an admin. Now, finally, you would bind to that location:

<firebase-auth user="{{user}}"></firebase-auth>
<firebase-document path="/admins/[[user.uid]]" data="{{isAdmin}}"></firebase-document>

The data will be null for non-admins and true for admins when signed in. Another note is that you must not rely on UI switching alone for admin control. You should write your security rules such that only a user with a uid in the admins node can write to admin-protected areas of your database.