Blog

How to Load Data Objects Belonging to the Logged-In User (Owner Policy)

by on September 1, 2019

A user on StackOverflow asked how to load only the data that belongs to the currently logged-in user. This is indeed an interesting and very common use-case. Backendless handles it beautifully and this feature certainly deserves a place among our Recipes.

What you need to know before we get to the coding sample part is:

Loading data objects that belong to a user does not require any specialized APIs. In fact, exactly the same Backendless API you’d use to load objects from the server should be used. The trick is in setting up the security policy to restrict users from accessing data objects and letting Backendless fall back on what we call the Owner Policy.

Consider the example below. The code logs in a user and adds a few data objects:

    private static void loginUserAsync() {
       Backendless.UserService.login("spidey@backendless.com", "mypassword", new AsyncCallback<BackendlessUser>() {
           @Override
           public void handleResponse(BackendlessUser backendlessUser) {
               Log.i(TAG, "User has been logged in");
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       });
    }
    
    private static void addObjectsAsync() {
       Order order1 = new Order();
       order1.orderName = "spider web";
       order1.orderNumber = 1;
    
       Order order2 = new Order();
       order2.orderName = "costume";
       order2.orderNumber = 2;
    
       List<Order> orders = Arrays.asList(order1, order2);
    
       Backendless.Data.of(Order.class).create(orders, new AsyncCallback<List<String>>() {
           @Override
           public void handleResponse(List<String> ids) {
               for (String id : ids) {
                   Log.i(TAG, "Order object has been saved with ID - " + id);
               }
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       });
    }

    private fun loginUserAsync() {
       Backendless.UserService.login("spidey@backendless.com", "mypassword", object : AsyncCallback<BackendlessUser> {
           override fun handleResponse(backendlessUser: BackendlessUser) {
               Log.i(TAG, "User has been logged in")
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       })
    }
    
    private fun addObjectsAsync() {
       val order1 = Order("spider web", 1)
       val order2 = Order("costume", 2)
    
       val orders = listOf(order1, order2)
    
       Backendless.Data.of(Order::class.java).create(orders, object : AsyncCallback<List<String>> {
           override fun handleResponse(ids: List<String>) {
               for (id in ids) {
                   Log.i(TAG, "Order object has been saved with ID - $id")
               }
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       })
    }

    Backendless.shared.userService.login(identity: "spidey@backendless.com", password: "mypassword", responseHandler: { loggedInUser in
                
        let order1 = Order()
        order1.orderName = "spider web"
        order1.orderNumber = 1
                
        Backendless.shared.data.of(Order.self).save(entity: order1, responseHandler: { savedOrder1 in
            if let savedOrder1 = savedOrder1 as? Order {
                print("Order 1 has been saved: \(savedOrder1.orderName ?? "") - \(savedOrder1.orderNumber ?? 0)")
            }
        }, errorHandler: { fault in
            print("Error: \(fault.message ?? "")")
        })
                
        let order2 = Order()
        order2.orderName = "costume"
        order2.orderNumber = 2
                
        Backendless.shared.data.of(Order.self).save(entity: order2, responseHandler: { savedOrder2 in
            if let savedOrder2 = savedOrder2 as? Order {
                print("Order 2 has been saved: \(savedOrder2.orderName ?? "") - \(savedOrder2.orderNumber ?? 0)")
            }
        }, errorHandler: { fault in
            print("Error: \(fault.message ?? "")")
        })
                
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })

    const Backendless = require('backendless')
    /*
     Or use `import Backendless from 'backendless'` for client side.
     If you don't use npm or yarn to install modules, you can add the following line
     <script src="//api.backendless.com/sdk/js/latest/backendless.min.js"></script>
     to your index.html file and use the global Backendless variable.
    */
    
    Backendless.initApp('YOUR_APP_ID', 'YOUR_JS_API_KEY')
    
    const addObjects = () => {
      const order1 = {
        orderName: 'spider web',
        orderNumber: 1
      }
    
      const order2 = {
        orderName: 'costume',
        orderNumber: 2
      }
    
      const storage = Backendless.Data.of('Order')
    
      return Promise.all([
        storage.save(order1),
        storage.save(order2),
      ])
    }
    
    const loginUser = () => {
      return Backendless.UserService.login('spidey@backendless.com', 'mypassword')
    }
    
    const onSuccess = () => {
      console.log('Order objects has been saved')
    }
    
    const onError = error => {
      console.error('Server reported an error: ', error.message)
      console.error('error code: ', error.code)
      console.error('http status: ', error.status)
    }
    
    Promise.resolve()
      .then(loginUser)
      .then(user => console.log('User has been logged in'))
      .then(addObjects)
      .then(onSuccess)
      .catch(onError)
    

    static void _loginUserAsync() =>
       Backendless.UserService.login("spidey@backendless.com", "mypassword").then(
         (user) => print("User has been logged in"));
    
    void _addObjectsAsync() {
      Map order1 = {
        "orderName": "spider web",
        "orderNumber": 1,
      };
      Map order2 = {
        "orderName": "costume",
        "orderNumber": 2,
      };
    
    
      Backendless.Data.of("Order").create([order1, order2]).then((ids) {
        ids.forEach((id) => print("Order object has been saved with ID - $id"));
      });
    }

    The Order class that the code above uses can be found below.

      The class uses public fields, but could also be turned into a Java Bean with getter and setter methods – Backendless supports either one of the approaches.

      public class Order {
         public String orderName;
         public int orderNumber;
      }

      public class Order {
         public String orderName;
         public int orderNumber;
      }

      @interface Order : NSObject
      
      @property (strong, nonatomic) NSString *orderName;
      @property (strong, nonatomic) NSNumber *orderNumber;
      
      @end

      @objcMembers class Order: NSObject {
          var orderName: String?
          var orderNumber: NSNumber?
      }


      The JS examples use the dictionary-based approach to work with the database and don’t need the Order class.

      The Flutter example uses the map-based approach to work with the database and doesn’t need the Order class.

      If you didn’t have the Order table in your app when you run the code, you will not have any other Order objects in the table. As a result, to see the effect of loading the objects that belong to the user, it would help to add an additional object belonging to someone else (or no one).

      Take a look at the Order table in Backendless Console – you will see it contains the ownerId column. That column is automatically added to every table in Backendless. If it contains a value, it will be the objectId of the user who created (saved) the object. Add a new row to the Order table using the Console and make sure the ownerId column remains empty. Your Order table should look like in the screenshot below (the “fish and chips” order was added manually):


      The code to load objects from the Order table is below. Make sure the user is logged in before running the code:

        // make sure the user is logged in. Use the code sample shown above
        
        Backendless.Data.of(Order.class).find(new AsyncCallback<List>() {
           @Override
           public void handleResponse(List orders) {
               for (Order order : orders) {
                   Log.i(TAG, "Order name - " + order.orderName);
                   Log.i(TAG, "Order number  - " + order.orderNumber);
                   Log.i(TAG, "============================");
               }
           }
        
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
        });

        // make sure the user is logged in. Use the code sample shown above
        
        [[Backendless.shared.data of:[Order class]] findWithResponseHandler:^(NSArray *orders) {
            for (Order *order in orders) {
                NSLog(@"Order name - %@", order.orderName);
                NSLog(@"Order number  - %@", order.orderNumber);
                NSLog(@"============================");
            }
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];

        // make sure the user is logged in. Use the code sample shown above
        
        Backendless.Data.of(Order::class.java).find(object : AsyncCallback<List<Order>> {
           override fun handleResponse(orders: List<Order>) {
               for (order in orders) {
                   Log.i(TAG, "Order name - ${order.orderName}")
                   Log.i(TAG, "Order number  - ${order.orderNumber}")
                   Log.i(TAG, "============================")
               }
           }
        
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
        })

        // make sure the user is logged in. Use the code sample shown above
            
        Backendless.shared.data.of(Order.self).find(responseHandler: { orders in
            if let orders = orders as? [Order] {
                for order in orders {
                    print("Order name - \(order.orderName ?? "")")
                    print("Order number - \(order.orderNumber ?? 0)")
                    print("============================")
                }
            }       
        }, errorHandler: { fault in
            print("Error: \(fault.message ?? "")")
        })

        const Backendless = require('backendless')
        /*
         Or use `import Backendless from 'backendless'` for client side.
         If you don't use npm or yarn to install modules, you can add the following line
         <script src="//api.backendless.com/sdk/js/latest/backendless.min.js"></script>
         to your index.html file and use the global Backendless variable.
        */
        
        Backendless.initApp('YOUR_APP_ID', 'YOUR_JS_API_KEY')
        
        const loginUser = () => {
          return Backendless.UserService.login('spidey@backendless.com', 'mypassword')
        }
        
        const getObjects = () => {
          return Backendless.Data.of('Order').find()
        }
        
        const onSuccess = orders => {
          orders.forEach(order => {
            console.log('Order name - ' + order.orderName)
            console.log('Order number  - ' + order.orderNumber)
            console.log('============================')
          })
        }
        
        const onError = error => {
          console.error('Server reported an error: ', error.message)
          console.error('error code: ', error.code)
          console.error('http status: ', error.status)
        }
        
        Promise.resolve()
          .then(loginUser)
          .then(user => console.log('User has been logged in'))
          .then(getObjects)
          .then(onSuccess)
          .catch(onError)
        

        // make sure the user is logged in. Use the code sample shown above
        
        Backendless.Data.of("Order").find().then((orders) {
          orders.forEach((order) {
            print("""Order name - ${order['orderName']}
        Order number  - ${order['orderNumber']}       
        ============================""");
          });
        });

        If you run the code before making any changes in the security policy, it will produce the following output:

        Order name - fish and chips
        Order number  - 3
        ============================
        Order name - costume
        Order number  - 2
        ============================
        Order name - spider web
        Order number  - 1
        ============================

        As you can see, this is not the desired output – the code loads all objects from the table. To make the change so the client app gets only the objects that belong to the user, follow the steps below:

        1. Locate the table for which you would like to make the restriction in the Data screen of Backendless Console. In this case, it will be the Order table. Click the Table Schema and Permissions button in the upper right corner.
        2. Click the Roles Permissions menu.
        3. Locate the AuthenticatedUser role and click the cell at the intersection of that role and the Find column until you see the icon for denying the operation (red X):
        4. Once the change is made, re-run the same code for fetching the data from the table. You will see that the code now returns only the objects that belong to the currently logged in user.

        Leave a Reply