Monday, August 19, 2013

OAuth testing discussion continued.

In the previous posts we discussed that OAuth testing depends on the nature of the token. The token is granted to a client for access to a user's resources. Therefore, OAuth APIs are largely qualified by user first and then client. So APIs are resource qualified in the form '/v1/users/me/clients/clientId'.  The reverse order of '/v1/clients' is generally not needed to be maintained because the clients don't have any access unless a user authorizes a resource.
That said, it is important to consider admin access such as one that can reach across users. Is it necessary to have admin access ? or should users manage their own client authorizations ? This is the topic we will briefly consider.
Resources that are not user-specific are generally less demanding on security as opposed to resources that are user owned. For example, the reward cards and balances on them that a user has is considered confidential and so no other user should have access to the same. However, what if there is governance required such as when the law enforcement agencies want to know how may coffee drinks you need to have before you get your free one ? Or even more pertinent where was your last purchase store in case you died ? Governance has a notorious connotation to big brother snooping but in our technical discussion here we refrain from such discussion and consider the case where it is important for another user or system to know information of other users. This could be as benign as friends sharing card information to see which card needs to be used first for the free drink. In OAuth spec, there is no limitation against admin access or access to other users information. In OAuth there is no mention for grouping users or clients. In OAuth there is no mention for API aliasing or shortening or parameter abbreviation for ease of use by one or more users especially from handheld devices. This is left to individual vendors to determine.
In the case where vendors choose to have cross user access or admin client access, the nature of the token granted is relevant. This could be scoped to include certain information such as last store while excluding certain information as credit cards or social security number. Basically, the protocol does not and should not prevent granularity of access and accessors be it the client or the user.
This definitely calls for OAuth tests so that security, functionality and performance are not affected. The RFC discusses several security implications from the user and client point of view however testing may need to be customized to the implementation. The implementation can be as simple as requiring all authorizations owned and delegated to the end user. 
Bearer token recommendations from RFC
1) Bearer tokens must be safeguarded since it gives access to the bearer. It should not appear in the clear such as in the header or in cookies. It should be passed only over secured traffic.
2) TLS certificate chains should be validated otherwise DNS hijackers can steal the token and gain unintended access
3) https should be used for all OAuth communications. The transport layer security is necessary for encrypting the traffic and securing the endpoints.
4) Bearer tokens should not be stored in the cookies. Implementations must not store bearer tokens in cookies because it can lead to cross site forgery attacks.
5) Bearer tokens should only be issued as short lived. One hour or less is recommended. Using short lived bearer tokens means that very few will gain access to it to misuse it.
6) Bearer tokens should always be scoped, scoping their use to the designated user or party. This is important because we don't want to grant universal access.
7) Bearer tokens should not be passed in page URLs since browses, web servers, and other software may not adequately secure URLS. The token may appear in web server logs and other data structures.
Appropriate error codes must be returned to deny specific requests. These include:
1) invalid_request - resource access error response This covers cases where the request is to be denied on grounds of invalid user or client. Http Status code is 400
2) invald_token - This is also a resource access error response. This covers the case where the token may have expired or the client has used a fabricated token. Http Status code is 401 (unauthorized)
3) insufficient_scope - this is also a resource access error response. This covers the case where the token has not been scoped and may cause security vulnerability. Http Status code is 403 (forbidden).
An example of a successful response could be
     HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"mF_9.B5f-4.1JqM",
       "token_type":"Bearer",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
     }


Sunday, August 18, 2013

OAuth testing discussion continued

The previous post on OAuth testing discussed the mapping of user and client for grant of token. Therefore testing discussed mostly on the setup of a user and client for the purpose of grant token. The token has a certain expiry time. There was some mention about scope and state. In this post, we discuss the revokes
When the authorization website grants access to a client from a user, the client can get a "bearer" token to access the protected resources. This token can be used by anyone for any of the APIs.  The authorization server must implement TLS so that the passwords and bearer tokens can flow.
OAuth only mandates that the token are issued for less than an hour. However clients, user and the authorization website could establish trust relationship the first time the authorization occurs. This is outside the scope of OAuth and may be provided as a mere convenience to the user from having to sign on each time for the client.
If this relationship were to be persisted it would merely be a table mapping the client with the user on this server. The records could be added when the authorization completes and revoked when the user explictly wants to clean up the clients she has authorized. The authorization website identifies the user by the user id and the client by the client id. When the client requests the authorization again subsequently, the website may have the user signed on from a previous session and hence uses the same to generate a token for the client. Neither the user specifies password again, nor does the client change request anything different other than accepting the redirect uri response with the token it is expecting.
Care should be taken to ensure that retries by the user do not create duplicate rows in the table maintained by the authorization website. In addition to user explicit revokes, the website may grant additional session time than the usual token expiry time. This can be facilitated with the use of a timestamp.
Lastly the website may do periodic cleanups and archival of the trust relationships. The default case could be to not store the trust relationships and have the token expirty time force re-authentication.
In the previous post, there was a sample code for objective C. The XCode project comes with a test  project that can be used for testing the code in the project. Here is an example for the code in the test:

//
//  Starbucks_API_ExplorerTests.m
//  Starbucks API ExplorerTests


#import "Starbucks_API_ExplorerTests.h"

@implementation Starbucks_API_ExplorerTests

- (void)setUp
{
    [super setUp];
}

- (void)tearDown
{
    // Tear-down code here.
    
    [super tearDown];
}

- (void)testExample
{
    STFail(@"Unit tests are not implemented yet in Starbucks API ExplorerTests");
}

@end



Saturday, August 17, 2013

sample objective c code


//
//  ViewController.m


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize userInput;
@synthesize userParams;
@synthesize userOutput;
@synthesize username;
@synthesize password;

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)Submit:(id)sender {
    ResponseViewer.text =  [self makeRequest];
}


- (NSString*) getAccessToken {
    NSString *urlString = @"https://test.openapi.starbucks.com/v1/oauth/token";
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"POST"];
    
    NSMutableData *body = [NSMutableData data];
    NSString *requestBody = [NSString stringWithFormat:@"grant_type=password&username=%@&password=%@client_id=my_client_id&client_secret=my_client_secret&scope=test_scope&api_key=my_api_key", username.text, password.text];

    [body appendData:[[NSString stringWithFormat:@"%@\r\n", requestBody] dataUsingEncoding:NSUTF8StringEncoding]];
    
    // set request body
    [request setHTTPBody:body];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    
    //return and test
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
    
    NSLog(@"%@", returnString);
    
    NSData *JSONData = [returnString dataUsingEncoding:NSUTF8StringEncoding];
    
    userOutput.text = returnString;
    
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:nil];
    
    return [json objectForKey:@"access_token"];
    
}

- (NSString*) makeRequest {
    NSString *urlString = [NSString stringWithFormat:@"https://test.openapi.starbucks.com/%@", userInput.text];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:[NSString stringWithFormat:@"%@", _method.accessibilityValue]];
    
    if ([_outputType.accessibilityValue compare:@"Json" options:NSCaseInsensitiveSearch] == TRUE)[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    
    if ([_outputType.accessibilityValue compare:@"XML" options:NSCaseInsensitiveSearch] == TRUE)[request setValue:@"application/xml" forHTTPHeaderField:@"Accept"];
    
    
    if ([_method.accessibilityValue compare:@"POST" options:NSCaseInsensitiveSearch] == TRUE)
    {
        NSMutableData *body = [NSMutableData data];
        NSString *requestBody = userParams.text;
    
        [body appendData:[[NSString stringWithFormat:@"%@\r\n", requestBody] dataUsingEncoding:NSUTF8StringEncoding]];
    
        // set request body
        [request setHTTPBody:body];
    }
    
    //return and test
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
    
    NSLog(@"%@", returnString);
    userOutput.text = returnString;
    return returnString;
    
}
    
@end

In the previous post we talked about database transforms of test results and mail. This was easy to implement when you apply the xslt transform. The resulting procedure with cursors looks something like this:

DECLARE @results table(
TestRunId uniqueidentifier NOT NULL,
SubmittedBy varchar(30) NOT NULL,
TestOwner varchar(30) NOT NULL,
TestName varchar(255) NOT NULL,
Environment varchar(10) NOT NULL,
TestCategory varchar(255) NOT NULL,
Submitted varchar(255) NOT NULL,
Status varchar(10) NOT NULL,
Elapsed varchar(255) NOT NULL,
Total int NOT NULL,
Passed int NOT NULL,
Fail int NOT NULL,
Inconclusive int NOT NULL,
Error int NOT NULL,
PercentPass varchar(10) NOT NULL,
ResultLabelWidth varchar(10) NOT NULL);

DECLARE @TestRunId uniqueidentifier ;
DECLARE @SubmittedBy varchar(30) ;
DECLARE @TestOwner varchar(30) ;
DECLARE @TestName varchar(255) ;
DECLARE @Environment varchar(10) ;
DECLARE @TestCategory varchar(255) ;
DECLARE @Submitted varchar(255) ;
DECLARE @Status varchar(10) ;
DECLARE @Elapsed varchar(255) ;
DECLARE @Total int ;
DECLARE @Passed int ;
DECLARE @Fail int ;
DECLARE @Inconclusive int ;
DECLARE @Error int ;
DECLARE @PercentPass varchar(10) ;
DECLARE @ResultLabelWidth varchar(10);


INSERT into @results SELECT * FROM dbo.Results;  -- or exec stored_proc
DECLARE @msg nvarchar(MAX);
SET @msg = '<head></head><body>
<div>Test Run Report</div><table id="ResultContainer" border="1">
<tr>
<th>Service Name</th>
<th>Environment</th>
<th>Test Category</th>
<th>Submitted By</th>
<th>Date</th>
<th>Status</th>
<th>Elapsed</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Incon</th>
<th>Error</th>
<th>Result</th>
<th>Test Owner</th>
</tr>'
DECLARE Results_Cursor CURSOR FOR  (SELECT * From @results); -- exec [ms01806.sbweb.prod].TestRunner.dbo.usp_GetSubmittedTestStatus @SubmittedBy='Ravi Rajamani (CW)',@TodayOnly=1,@TestType=N'Coded';
OPEN Results_Cursor;
FETCH NEXT FROM Results_Cursor INTO @TestRunId, @SubmittedBy , @TestOwner , @TestName, @Environment , @TestCategory , @Submitted , @Status , @Elapsed , @Total , @Passed , @Fail , @Inconclusive , @Error , @PercentPass , @ResultLabelWidth ;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @msg = @msg + '<tr>
                            <td>' +  @TestName + '
                            </td>
                            <td> ' + @Environment +  '
                            </td>
                            <td>' + @TestCategory + '
                            </td>
                            <td>'  + @SubmittedBy + '
                            </td>
                            <td>' + @Submitted + '
                            </td>
                            <td> '  +  @Status + '
                            </td>
                            <td>' + @Elapsed + '</td>
                            <td>' + Convert(nvarchar, @Total) + '
                            </td>
                            <td>' + Convert(nvarchar, @Passed) + '
                            </td>
                            <td>'  + Convert(nvarchar, @Fail) + '
                            </td>
                            <td> ' + Convert(nvarchar, @Inconclusive) + '
                            </td>
                            <td>' + Convert(nvarchar, @Error) + '
                            </td>
                            <td>' + @PercentPass + '
                            </td>
                            <td> ' + @TestOwner + '
                            </td>
                        </tr>';
FETCH NEXT FROM Results_Cursor INTO @TestRunId, @SubmittedBy , @TestOwner , @TestName, @Environment , @TestCategory , @Submitted , @Status , @Elapsed , @Total , @Passed , @Fail , @Inconclusive , @Error , @PercentPass , @ResultLabelWidth ;
END
SET @msg = @msg + '</table></body>';
CLOSE Results_Cursor;
DEALLOCATE Results_Cursor;

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'Mail Profile',
    @recipients = 'recipients@where.com',
@body_format = 'HTML',
    @body = @msg,
    @subject = 'Test Results Reports' ;


Friday, August 16, 2013

automating test results publishing from the database.

Test runs generate results that are often important for keeping a daily pulse on the software quality. Typically such data is published in a database and have details such as test run id, submitted by, test owner, test name, environment, test category, submitted, status, Elapsed time, total tests passed, total tests failed, inconclusive tests, Tests that have errors, percentage pass etc. These records give sufficient detail about the tests that were run and they just need to be transformed into xslt to make pretty reports. Both the xslt transform and associated css if any could be the text template with which the data is presented. The individual spans for each column may need to have distinguished identifiers so that the transform can be rendered as an html. These identifiers could all have row number as suffix to distinguish them.  A header row followed by one or more rows for the data is sufficient to display the test run results. The table itself could use a css class to make it prettier by differentiating alternate rows. This is done with a class id and a style borrowed from the css. The transform itself could be wrapped in a stored procedure which retrieves the results from the database and enumerates them. This stored procedure has several advantages. First this logic is as close to the data as possible. Second it can be invoked by many different clients on scheduled or on demand basis. Third, it can be used to register a trigger for the insert of results from well marked runs. Runs can be marked based on some attributes such as a login for automated test execution from reporting. This allows regular test invocation and publish by all users without any reporting. Only the test execution by a separate login is candidate for reporting. The stored procedure may need to enumerate through the records. This can be done with a cursor. A cursor is also useful in working with one record at a time. The entire stored procedure will have plans cached for improved performance over adhoc TSQL. Therefore this will have performance improvements as well. Finally the database itself can send out mails with this html text. This can be done with a database mail  profile set up with an smtp server. Setting up automated e-mails requires the use of sysadmin or appropriate server scoped privileges.
If transforms are not needed, the results from the queries or stored procedures can be directly sent out. This may be a quick and easy thing to do since the results can be output to text format and imported via excel and other BI applications.
Also, test results are often associated with team foundation server work items or queries. Such articles might involve items such as active and resolved bug counts. While it is easier to use the Excel application to open and format such TFS queries, it is also possible to directly issue http requests from the database. This consolidates the results and the logic for results reporting in one place.