Blog

How to load data objects belonging to the logged-in user (Owner Policy)

by on March 8, 2015

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:

    Asynchronous sample (Android and Plain Java):

    private static void loginUserAsync()
    {
        Backendless.UserService.login( "spidey@backendless.com", "mypassword", new AsyncCallback<BackendlessUser>()
        {
            @Override
            public void handleResponse( BackendlessUser backendlessUser )
            {
               System.out.println( "User has been logged in" );
            }
            @Override
            public void handleFault( BackendlessFault backendlessFault )
            {
                System.out.println( "Server reported an error - " + backendlessFault.getMessage() );
            }
        } );
    }
    private static void addObjectsAsync()
    {
        AsyncCallback<Order> callback = new AsyncCallback<Order>()
        {
            @Override
            public void handleResponse( Order order )
            {
                System.out.println( "Order object has been saved - " + order.orderName );
            }
            @Override
            public void handleFault( BackendlessFault backendlessFault )
            {
                System.out.println( "Server reported an error - " + backendlessFault.getMessage() );
            }
        };
        Order order1 = new Order();
        order1.orderName = "spider web";
        order1.orderNumber = 1;
        Backendless.Data.of( Order.class ).save( order1, callback );
        Order order2 = new Order();
        order2.orderName = "costume";
        order2.orderNumber = 2;
        Backendless.Data.of( Order.class ).save( order2, callback );
    }

    Synchronous sample (Plain Java only):

    private static void loginUserSync()
    {
        Backendless.UserService.login( "spidey@backendless.com", "mypassword" );
    }
    private static void addObjectsSync()
    {
        Order order1 = new Order();
        order1.orderName = "spider web";
        order1.orderNumber = 1;
        Backendless.Data.of( Order.class ).save( order1 );
        Order order2 = new Order();
        order2.orderName = "costume";
        order2.orderNumber = 2;
        Backendless.Data.of( Order.class ).save( order2 );
    }

    [Backendless.shared.userService loginWithIdentity:@"spidey@backendless.com" password:@"mypassword" responseHandler:^(BackendlessUser *loggedInUser) {
            
        Order *order1 = [Order new];
        order1.orderName = @"spider web";
        order1.orderNumber = @1;
            
        [[Backendless.shared.data of:[Order class]] saveWithEntity:order1 responseHandler:^(Order *savedOrder1) {
            NSLog(@"Order 1 has been saved: %@ - %@", savedOrder1.orderName, savedOrder1.orderNumber);
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
            
        Order *order2 = [Order new];
        order2.orderName = @"costume";
        order2.orderNumber = @2;
            
        [[Backendless.shared.data of:[Order class]] saveWithEntity:order2 responseHandler:^(Order *savedOrder2) {
            NSLog(@"Order 2 has been saved: %@ - %@", savedOrder2.orderName, savedOrder2.orderNumber);
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
            
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", 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 ?? "")")
    })

    
    

    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.

      package com.mbaas.sample;
      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?
      }

      
      

      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:

        Asynchronous sample (Android and Plain Java):

        // make sure the user is logged in. Use the code sample shown above
        Backendless.Data.of( Order.class ).find( new AsyncCallback<BackendlessCollection<Order>>()
        {
            @Override
            public void handleResponse( BackendlessCollection<Order> users )
            {
                Iterator<Order> userIterator = users.getCurrentPage().iterator();
                while( userIterator.hasNext() )
                {
                    Order order = userIterator.next();
                    System.out.println( "Order name - " + order.orderName );
                    System.out.println( "Order number  - " + order.orderNumber );
                    System.out.println( "============================" );
                }
            }
            @Override
            public void handleFault( BackendlessFault backendlessFault )
            {
                System.out.println( "Server reported an error - " + backendlessFault.getMessage() );
            }
        } );

        Synchronous sample (Plain Java only):

        // make sure the user is logged in. Use the code sample shown above
        BackendlessCollection<Order> orders = Backendless.Data.of( Order.class ).find();
        Iterator<Order> userIterator = orders.getCurrentPage().iterator();
        while( userIterator.hasNext() )
        {
            Order order = userIterator.next();
            System.out.println( "Order name - " + order.orderName );
            System.out.println( "Order number  - " + order.orderNumber );
            System.out.println( "============================" );
        }

        // 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.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 ?? "")")
        })

        
        

        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.