Category Archives: Programming

Revisiting Docker – How to use docker container in ECS

In post How to build docker image for your application, we saw how to create a docker container image for your application and then run that image. In this post, I want to revisit some of the docker commands and how to use this docker container in ECS (Elastic Container Service) which is offered by Amazon.

  1. Publishing your docker container image in ECR (Elastic Container Repository) – Amazon offers a repository where you can push your docker image to store. Once you have the image in the repository, it can be used in ECS.
docker tag source_image:[tag] target_image:[tag]

docker push image:[tag] repository:image

2. You can pull this image from repository to run on your local as below

docker pull image:[tag]

3. Once you have docker image published in the repository, it is a straightforward. Create a cluster in ECS.

4. Cluster is nothing but collection of multiple services running on their own EC2 instances, provided you create a service of type EC2. Once you create a cluster, you can create a service. Another type of service you can create is that of Fargate. But we will not talk about it in this post.

5. When you create a service, it will ask you to enter the value for task, that means you have to create a task first. Enter the name and type of task (EC2).

6. Provide the docker container image and any environment variables you need to run this docker container. Provide necessary details of how much memory you need and if you are using storage.

7. Once you create a task, you can use it creating a service from Step 5. Choose an application load balancer in your service. If you haven’t already created load balancer and target group, you will need to create those. Use the newly created load balancer and target group for your service.

8. Now once everything is created, you can start the task to run. So this service will be available in the cloud. Amazon offers a healthcheck for your service, that you can configure while configuring service.

Conclusion –

In this post, I showed how to use Amazon ECS service for creating a service and running that service in Amazon cloud. If you want to learn about docker containers, learn here.

 

Social login with Spring Boot

In this post, I will show how to use social login in a Spring Boot application. So we build an application, but we use a form-based login which is the most basic and most insecure authentication mechanism out there. How do we get over this hunch and use the latest more secure mechanism?

Social login – Tada.

Yes, with an increasing number of social networks, it has become increasingly popular and easier to build an OAuth based login mechanism using social networks. In other words, spring boot offers a solution with a social login plugin and in this post, we will show how to use social login to authenticate your users.

What will you need

  • IntelliJ
  • Java 8
  • Twitter/Facebook/Google/Linkedin/Github accounts
  • Spring Boot
  • Gradle

Spring Social Core

Spring offers a spring-social-core project that contains APIs to connect to user’s social accounts. Nevertheless, this library includes a connect framework that offers a solution to manage connections with social service providers. It offers support for OAuth1a and OAuth2. The simplest way to understand this library is that you create a connection factory for each social provider. A connection factory locator finds a factory to create a Sign In Provider. I will provide more details as we go along in implementing this module.

 

Create a Social Login Gradle Project

If you haven’t noticed from my blog posts, but I have switched from eclipse to IntelliJ for programming editor. Intellij is just smarter and easy to write code editor. So first create a Gradle project for spring boot. (Side note – if you are using IntelliJ ultimate edition, it offers a feature to create spring project.) We will be using the latest version of Spring Boot (2.0.3.RELEASE) to build this project.

The gradle file will look like below:

buildscript {
  ext {
    springBootVersion = '2.0.3.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.betterjavacode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
  mavenCentral()
  maven {
    url 'https://repo.spring.io/libs-milestone'
  }
}


dependencies {
  compile('org.springframework.boot:spring-boot-starter-thymeleaf')
  compile("org.springframework.boot:spring-boot-starter-web")
  compile("org.springframework.social:spring-social-security:1.1.6.RELEASE")
  compile("org.springframework.social:spring-social-config:1.1.6.RELEASE")
  compile("org.springframework.social:spring-social-core:1.1.6.RELEASE")
  compile('org.springframework.boot:spring-boot-starter-security')
  compile('org.springframework.boot:spring-boot-starter-data-jpa')
  compile("com.fasterxml.jackson.core:jackson-databind:2.9.6")
  compile('mysql:mysql-connector-java:5.1.6')
  compile("org.springframework.social:spring-social-twitter:1.1.2.RELEASE")
  compile("org.springframework.social:spring-social-facebook:2.0.3.RELEASE")
  compile("org.springframework.social:spring-social-google:1.0.0.RELEASE")
  compile("org.springframework.social:spring-social-github:1.0.0.M4")
  compile("org.springframework.social:spring-social-linkedin:1.0.2.RELEASE")
  testCompile('org.springframework.boot:spring-boot-starter-test')
}

I will explain each dependency added in Gradle file as we go along.

Create an entity class

We will be using a simple entity class for User with just one field name.  This will look like below:

@JsonIgnoreProperties(ignoreUnknown = true)
public class User
{
    public String name;

    public String getName() 
    {
        return name;
    }
    public void setName(String name) 
    {
        this.name = name;
    }
}

Create A Social Configuration to adapt Spring Social library

Firstly, we will implement an interface SocialConfigurer that Spring social library offers. For instance, as part of this implementation, we will create connection factories for different social service providers. Also for this module, we are using InMemoryUsersConnectionRepository. You can always implement a JDBC based database user connection repository. This class will look like below:

package com.betterjavacode.reusablesociallogin;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.social.UserIdSource;
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurer;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.mem.InMemoryUsersConnectionRepository;
import org.springframework.social.connect.support.ConnectionFactoryRegistry;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.github.connect.GitHubConnectionFactory;
import org.springframework.social.google.connect.GoogleConnectionFactory;
import org.springframework.social.linkedin.connect.LinkedInConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;

import javax.inject.Inject;
import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:application.properties")
@EnableSocial
public class SocialConfig implements SocialConfigurer
{

    @Autowired
    private DataSource dataSource;

    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment)
    {
        connectionFactoryConfigurer.addConnectionFactory(new TwitterConnectionFactory(environment.getProperty("spring.social.twitter.consumerKey"), environment.getProperty("spring.social.twitter.consumerSecret")));
        connectionFactoryConfigurer.addConnectionFactory(new FacebookConnectionFactory(environment.getProperty("spring.social.facebook.appId"),environment.getProperty("spring.social.facebook.appSecret")));
        GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.appId"),environment.getProperty("spring.social.google.appSecret"));
        googleConnectionFactory.setScope("profile");
        connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
        connectionFactoryConfigurer.addConnectionFactory(new GitHubConnectionFactory(environment.getProperty("spring.social.github.appId"), environment.getProperty("spring.social.github.appSecret")));
        connectionFactoryConfigurer.addConnectionFactory(new LinkedInConnectionFactory(environment.getProperty("spring.social.linkedin.appId"), environment.getProperty("spring.social.linkedin.appSecret")));
    }

    @Override
    public UserIdSource getUserIdSource()
    {
        return new UserIdSource() {
            @Override
            public String getUserId() {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication == null) {
                    throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
                }
                return authentication.getName();
            }
        };
    }

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator)
    {
        InMemoryUsersConnectionRepository usersConnectionRepository = new InMemoryUsersConnectionRepository(
                connectionFactoryLocator);
        return usersConnectionRepository;
    }

}

As you see in this class, I am referring to application.properties . The application.properties will look like below:

spring.social.twitter.consumerKey=[Twitter consumer key]
spring.social.twitter.consumerSecret=[Twitter consumer secret]
spring.social.facebook.appId=[Facebook client id]
spring.social.facebook.appSecret=[Facebook client secret]
spring.social.google.appId=[Google client id]
spring.social.google.appSecret=[Google client secret]
spring.social.github.appId=[Github client id]
spring.social.github.appSecret=[Github client secret]
spring.social.linkedin.appId=[Linkedin client id]
spring.social.linkedin.appSecret=[Linkedin client secret]
server.port = 8448

In other words, to get clientid and clientsecret , you will have to register your application with each social service provider. We will not be covering that in this post.

Create a spring web security configuration

In this class, we will extend websecurityconfigureradapter and configure HTTP security as part of spring security implementation. We also add a bean to create Sign In Providers which are part of Spring Social. In addition, we will implement this Sign In Provider to provide a facility to users to sign in with their social provider.

package com.betterjavacode.reusablesociallogin;


import com.betterjavacode.reusablesociallogin.social.SocialConnectionSignup;
import com.betterjavacode.reusablesociallogin.social.SocialSignInAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.mem.InMemoryUsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.security.SpringSocialConfigurer;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{

    @Autowired
    private ConnectionFactoryLocator connectionFactoryLocator;

    @Autowired
    private UsersConnectionRepository usersConnectionRepository;

    @Autowired
    private SocialConnectionSignup socialConnectionSignup;

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/","/socialloginhome","/signin/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Bean
    public ProviderSignInController providerSignInController()
    {
        ((InMemoryUsersConnectionRepository) usersConnectionRepository)
                .setConnectionSignUp(socialConnectionSignup);

        return new ProviderSignInController(
                connectionFactoryLocator,
                usersConnectionRepository,
                new SocialSignInAdapter());
    }
}

As you see in this class, we have a bean ProviderSignInController which will use SocialSignInAdapter.

Implement a Sign In Adapter

Above all, this is the heart of our implementation where authentication will take place and the user will be assigned a role to access the application. The user will be redirected to the application if the user successfully authenticates. This class will look like below:

package com.betterjavacode.reusablesociallogin.social;

import com.betterjavacode.reusablesociallogin.util.ConnectionHelper;
import com.betterjavacode.reusablesociallogin.util.ConnectionType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.NativeWebRequest;

import java.util.ArrayList;
import java.util.List;

@Service
public class SocialSignInAdapter implements SignInAdapter
{
    @Override
    public String signIn(String userId, Connection<?> connection, NativeWebRequest request)
    {
        Authentication authentication = getAuthentication(userId, connection);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        return "/socialloginsuccess";
    }

    private Authentication getAuthentication(String localUserId, Connection<?> connection)
    {
        List<GrantedAuthority> roles = getRoles(connection);

        String password = null;

        Authentication authentication = new UsernamePasswordAuthenticationToken(localUserId, password, roles);

        return authentication;
    }

    private List<GrantedAuthority> getRoles(Connection<?> connection)
    {
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();

        ConnectionType type = ConnectionHelper.getConnectionType(connection);

        String role = type.toString();

        roles.add(new SimpleGrantedAuthority(role));

        return roles;
    }
}

As you see in getAuthentication, we pass userId and roles for token-based authentication.

If the user has not signed up with a social provider before, he will be asked to sign up and will be redirected to the application after the first time sign up.

package com.betterjavacode.reusablesociallogin.social;

import com.betterjavacode.reusablesociallogin.entity.User;
import com.betterjavacode.reusablesociallogin.util.UserHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Service;

@Service
public class SocialConnectionSignup implements ConnectionSignUp
{
    @Autowired
    UserHelper userHelper;

    @Override
    public String execute(Connection<?> connection)
    {
        User user = userHelper.getUser(connection);

        return user.getName();
    }
}

As you see in this class, we have Autowired a userHelper class, this class will have an implementation to get user details from each social provider.

Therefore, this UserHelper will look like below:

package com.betterjavacode.reusablesociallogin.util;

import com.betterjavacode.reusablesociallogin.entity.User;

import org.springframework.social.connect.Connection;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.github.api.GitHub;
import org.springframework.social.google.api.Google;
import org.springframework.social.linkedin.api.LinkedIn;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.stereotype.Component;

@Component
public class UserHelper
{
    public User getUser(Connection<?> connection)
    {
        User user = null;

        //get the connection type
        ConnectionType type = ConnectionHelper.getConnectionType(connection);

        if (type.equals(ConnectionType.TWITTER)) {
            user = getTwitterUser(connection);
        } else if (type.equals(ConnectionType.FACEBOOK)) {
            user = getFacebookUser(connection);
        } else if (type.equals(ConnectionType.GOOGLE)) {
            user = getGoogleUser(connection);
        } else if (type.equals(ConnectionType.GITHUB)) {
            user = getGithubUser(connection);
        } else if (type.equals(ConnectionType.LINKEDIN)){
            user = getLinkedInUser(connection);
        }

        return user;
    }

    private User getTwitterUser(Connection<?> connection)
    {
        User user = new User();
        Twitter twitterApi = (Twitter)connection.getApi();

        String name = twitterApi.userOperations().getUserProfile().getName();

        user.setName(name);

        return user;
    }

    private User getFacebookUser(Connection<?> connection)
    {
        User user = new User();
        Facebook facebookApi = (Facebook)connection.getApi();
        String [] fields = { "name" };
        User userProfile = facebookApi.fetchObject("me", User.class, fields);

        String name = userProfile.getName();

        user.setName(name);

        return user;
    }

    private User getGoogleUser(Connection<?> connection)
    {
        User user = new User();
        Google googleApi = (Google) connection.getApi();
        String name = googleApi.plusOperations().getGoogleProfile().getDisplayName();
        user.setName(name);
        return user;
    }

    private User getGithubUser(Connection<?> connection)
    {
        User user = new User();
        GitHub githubApi = (GitHub) connection.getApi();
        String name = githubApi.userOperations().getUserProfile().getName();
        user.setName(name);
        return user;
    }

    private User getLinkedInUser(Connection<?> connection)
    {
        User user = new User();
        LinkedIn linkedInApi = (LinkedIn) connection.getApi();
        String name = linkedInApi.profileOperations().getUserProfile().getFirstName();
        user.setName(name);
        return user;
    }
}

Implementing a controller and views

Similarly, the last piece in this puzzle is to add a controller and corresponding views so when the user accesses the application, the user will be challenged for authentication.

However, we will add a login controller which will have three views for login, sociallogin and socialloginsuccess . This will look like below:

@Controller
public class LoginController
{
    @RequestMapping(value="/login", method= RequestMethod.GET)
    public String login(Model model)
    {
        return "login";
    }

    @RequestMapping(value ="/socialloginhome", method = RequestMethod.GET)
    public String socialloginhome(Model model)
    {
        return "socialloginhome";
    }

    @RequestMapping(value="/socialloginsuccess", method= RequestMethod.GET)
    public String socialloginsuccess(Model model)
    {
        return "socialloginsuccess";
    }
}

 

Running the application

Once I build the application and run it, the flow will look like below:

You click on hereit will take you to social login screen as below:

I will choose Facebook and server-side code will redirect me to Facebook login screen. Once I enter my credentials, Facebook will redirect back me to my application as below:

Hence, we showed successful social authentication. Lastly, social login is part of any saas application you are building.

Conclusion

In conclusion, we showed how to create a social login module using the Spring boot social feature. Moreover, the code for this will be available to download here.

References

  1. Spring Social Overview – Spring Social
  2. Spring Social development – Spring Social Development
  3. Spring Social tutorial – Spring Social Tutorial

 

LDAP Authentication with Spring Boot LDAP

In this article, I will show how to achieve LDAP authentication using spring boot plugin for LDAP.

What you will need

  • Java 8
  • IntelliJ
  • Apache Directory Server
  • Apache Directory Studio
  • Spring Boot

Implementation

To use LDAP for authentication with Spring Boot, definitely set up a LDAP server and we will use Apache Directory Server in our case. I will not be showing “How to set up and add LDIF entries” as this is entirely different topic. But I have two users John Doe and James Hook in my LDAP repository. I will use those accounts to login.

Set up Spring Boot with LDAP dependencies

As part of implementing this, we will add following gradle dependencies:

compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.ldap:spring-ldap-core')
compile('org.springframework.security:spring-security-ldap')
compile('org.springframework:spring-tx')
compile('org.apache.directory.server:apacheds-server-jndi:1.5.5')

For our task, spring-ldap-core and spring-security-ldap are important dependencies.

Rest Controller

We will create a simple rest controller that will display our sample page. This sample page will be secured and to access it, an user will have to authenticate.

package com.betterjavacode.SpringBootLdapApplication.Controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController
{
    @GetMapping("/")
    public String index()
    {
        return "Welcome to Spring Boot Ldap Application Page";
    }
}

Security Configuration

We will add our security configuration by adding a WebSecurityConfig class that extends WebSecurityConfigurerAdapter.

package com.betterjavacode.SpringBootLdapApplication;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.httpBasic().and().authorizeRequests().anyRequest().authenticated().and().formLogin().and().csrf().disable();
    }

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception
    {
        authenticationManagerBuilder
                .ldapAuthentication()
                .contextSource().url("ldap://localhost:10389/o=betterjavacode")
                .managerDn("uid=admin,ou=system").managerPassword("secret")
                .and()
                .userSearchBase("ou=people")
                .userSearchFilter("(uid={0})");
    }

}

Now if we look at this, when a user will launch the application, he will be challenged with a login form based on formLogin() . Once the user enters credentials, he will be redirected to home page.

The result for that will look like below:

Once the user enters credentials:

Conclusion

In this post, we showed how to use LDAP for authentication using Spring boot.

References

  1. Spring Boot LDAP – Spring Boot LDAP
  2. Authentication using LDAP – LDAP Authentication

 

Custom Twitter Client vs Spring Boot Twitter Plugin

To use twitter data in my saas application, I was going to write my own custom Twitter client by doing a rest call. However, I found out Spring Boot offers a Twitter plugin that can be used to fetch Twitter data. Neat.

In this post, I will show some comparison of these two approaches and why one can choose over another:

Custom Twitter Client

So custom twitter client will be a standalone client which will build an HTTP entity with client secrets that are needed to authenticate with Twitter API. In this client, we will use restOperations to call API endpoint passing HTTP entity and the REST call will respond with Twitter Data Model.

This will look like below:

public TwitterDataModel getTwitterData(long accountId)
{
    String url = buildRestUrl(accountId);
    ParameterizedTypeReference<HashMap<Long, TwitterDataModel>> responseType = new ParameterizedTypeReference<HashMap<Long, TwitterDataModel>>(){};
    HttpEntity entity = buildHttpEntity(CLIENT_ID, CLIENT_SECRET);
    Map<Long, TwitterDataModel> twitterDataModelMap = restOperations.exchange(url, HttpMethod.GET, entity, responseType).getBody();

    Long keyForData = new Long(accountId);
    TwitterDataModel twitterDataModel = twitterDataModelMap.get(keyForData);

    return twitterDataModel;
}

public String buildRestUrl(long accountId)
{
    return TWITTER_REST_ENDPOINT + accountId + TWITTER_REST_API;
}

There is nothing much wrong with this approach, except the fact that we will have to write an extra TwitterDataModel business object. Also, this business model should be created before we do the actual REST call.

Spring Boot Twitter Plugin

To use this plugin, first, we need to add the plugin in Gradle or maven like below:

compile('org.springframework.social:spring-social-twitter')

Once we have this plugin, we can add an object of type Twitter in our code to call REST APIs.

This will look like below:

private final Twitter twitter;

public TwitterDataModel getTwitterData(long accountId)
    {
        String url = buildRestUrl(accountId);
        ParameterizedTypeReference<HashMap<Long, TwitterDataModel>> responseType = new ParameterizedTypeReference<HashMap<Long, TwitterDataModel>>(){};
        HttpEntity entity = buildHttpEntity(CLIENT_ID,CLIENT_SECRET);

        Map<Long, TwitterDataModel> twitterDataModelMap = twitter.restOperations().exchange(url, HttpMethod.GET, entity, responseType).getBody();

        Long keyForData = new Long(accountId);
        TwitterDataModel twitterDataModel = twitterDataModelMap.get(keyForData);

        return twitterDataModel;
    }

    public String buildRestUrl(long accountId)
    {
        return TWITTER_REST_ENDPOINT + accountId + TWITTER_REST_API;
    }

The major advantage of this plugin is that we can get the data in Twitter Data Model that twitter offers. An then we can go on to use to handle our data.

Conclusion

In this post, I showed how we can use a Spring Boot Twitter social plugin to gather Twitter data.

SaaS Application Design Discussion – Part IV

In the previous post, I discussed database design for saas application. To continue design discussion for our social pie saas application, in this post, we will discuss a few more ideas about how a user and user’s company will sign up for application. This will be a user story. We are building a SAAS application. To make it more viable, this application will use the freemium and pay model.

  1. In the freemium model – Any company can join and review what reports it will be able to see and what kind of marketing strategies it can design using those reports.
    1. 5 Reports
    2. Free Marketing Strategies
    3. Up to 3 users
    4. Limited usage of twitter and Instagram APIs
  2. In pay model – If a company opts to join a pay subscription, it will be able to get more advance reports, will be able to see reports in a different format, and can also get consultation about strategies for marketing.
    1. N number of reports – Your data, your freedom
    2. Marketing Consultation
    3. KPI tracker and notification
    4. Up to N users (won’t be implemented in first version)

User Flow

Once the user lands on the home page, he can opt for either model and sign up. An automated email will be sent to the user for a demo or sign up. Upon sign up, where the user will be entering details about himself and his company. This user will be an administrator and he can add other users with custom roles. The same user can go to reports tab and click on sync data. This will get the latest data from social media and update it in the database. Every new request will compare newly fetched data with current data in the database. If the new request has brought changes, it will be updated in the database. When generating reports, this data from the database will be cached.

We will not be fetching any on-the-fly data from Twitter and Instagram. Administrator users will have an option to send reports to other people from the company. There will be an email/download option.

There are some nitty-gritty details that I have not covered in this post. But with this post, we will be starting to develop a Saas application using java and spring-boot.