Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updates report downloads to use accesstoken #293

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions AppSales.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,11 @@
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0430;
TargetAttributes = {
7F709D2613BC98860008DBAD = {
DevelopmentTeam = 43KR46Q4ST;
};
};
};
buildConfigurationList = 7F709D2113BC98860008DBAD /* Build configuration list for PBXProject "AppSales" */;
compatibilityVersion = "Xcode 3.2";
Expand Down Expand Up @@ -2899,6 +2904,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
DEVELOPMENT_TEAM = 43KR46Q4ST;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "OtherSources/AppSales-Prefix.pch";
HEADER_SEARCH_PATHS = "";
Expand All @@ -2915,6 +2921,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
DEVELOPMENT_TEAM = 43KR46Q4ST;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "OtherSources/AppSales-Prefix.pch";
HEADER_SEARCH_PATHS = "";
Expand Down
5 changes: 3 additions & 2 deletions Classes/ASAccount.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
}

@property (nonatomic, assign) BOOL isDownloadingReports;
@property (nonatomic, retain) NSString *password; // this property encapsulates the keychain access,
// it is not actually stored in the Core Data model
@property (nonatomic, retain) NSString *token; // this property encapsulates the keychain access,
// it is not actually stored in the Core Data model
@property (nonatomic, retain) NSString *downloadStatus;
@property (nonatomic, assign) float downloadProgress;

Expand All @@ -36,6 +36,7 @@
@property (nonatomic, retain) NSNumber *paymentsBadge;

- (void)deletePassword;
- (void)deleteToken;
- (NSString *)displayName;

@end
40 changes: 35 additions & 5 deletions Classes/ASAccount.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,46 @@

#define kAccountKeychainServiceIdentifier @"iTunesConnect"

@interface ASAccount (PrimitiveAccessors)
- (NSString *)primitiveUsername;
@end

@implementation ASAccount

@dynamic username, vendorID, title, sortIndex, dailyReports, weeklyReports, products, payments, reportsBadge, paymentsBadge;
@synthesize isDownloadingReports, downloadStatus, downloadProgress;

- (NSString *)password
/*
* For backwards compatibility, allow existing usernames to continue to be used (for things like data folder names, etc). If
* no username is set, use the current token. If no token is set, use "default".
*/
- (NSString *)username
{
[self willAccessValueForKey:@"username"];
NSString *username = [self primitiveUsername];
[self didAccessValueForKey:@"username"];
if (!username || [username isEqualToString:@""]) {
if (self.token && ![self.token isEqualToString:@""]) {
return self.token;
}
return @"default";
}
return username;
}

- (NSString *)token
{
return [SSKeychain passwordForService:kAccountKeychainServiceIdentifier account:self.username];
return [SSKeychain passwordForService:kAccountKeychainServiceIdentifier account:kAccountKeychainServiceIdentifier];
}

- (void)setPassword:(NSString *)newPassword
- (void)setToken:(NSString *)token
{
[SSKeychain setPassword:newPassword forService:kAccountKeychainServiceIdentifier account:self.username];
[SSKeychain setPassword:token forService:kAccountKeychainServiceIdentifier account:kAccountKeychainServiceIdentifier];
}

- (void)deleteToken
{
[SSKeychain deletePasswordForService:kAccountKeychainServiceIdentifier account:kAccountKeychainServiceIdentifier];
}

- (void)deletePassword
Expand All @@ -36,7 +63,10 @@ - (NSString *)displayName
if (self.title && ![self.title isEqualToString:@""]) {
return self.title;
}
return self.username;
if (self.token && ![self.token isEqualToString:@""]) {
return self.token;
}
return @"Default";
}

@end
4 changes: 3 additions & 1 deletion Classes/AccountsViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#define kAccountUsername @"username"
#define kAccountPassword @"password"
#define kAccountToken @"token"
#define kAccountVendorID @"vendorID"
#define ASViewSettingsDidChangeNotification @"ASViewSettingsDidChangeNotification"

Expand Down Expand Up @@ -41,6 +42,7 @@
@property (nonatomic, retain) UIDocumentInteractionController *documentInteractionController;

- (void)reloadAccounts;
- (void)deleteAllAccountPasswords;
- (void)downloadReports:(id)sender;
- (void)doExport;
- (NSString *)folderNameForExportingReportsOfAccount:(ASAccount *)account;
Expand All @@ -55,4 +57,4 @@

- (void)accountsViewController:(AccountsViewController *)viewController didSelectAccount:(ASAccount *)account;

@end
@end
89 changes: 45 additions & 44 deletions Classes/AccountsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,21 @@ - (void)reloadAccounts
[self.tableView reloadData];
}

- (void)deleteAllAccountPasswords {
[self.accounts enumerateObjectsUsingBlock:^(ASAccount * _Nonnull account, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"deleting %@ pass", account.username);
[account deletePassword];
}];
[self saveContext];
}

- (void)downloadReports:(id)sender
{
for (ASAccount *account in self.accounts) {
if (account.password && account.password.length > 0) { //Only download reports for accounts with login
if (account.token && account.token.length > 0) { //Only download reports for accounts with login
if (!account.vendorID || account.vendorID.length == 0) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Vendor ID Missing", nil)
message:[NSString stringWithFormat:NSLocalizedString(@"You have not entered a vendor ID for the account \"%@\". Please go to the account's settings and fill in the missing information.", nil), [account displayName]]
message:[NSString stringWithFormat:NSLocalizedString(@"You have not entered a vendor ID for the account with token \"%@\". Please go to the account's settings and fill in the missing information.", nil), [account token]]
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", nil)
otherButtonTitles:nil] autorelease] show];
Expand Down Expand Up @@ -267,13 +275,12 @@ - (void)addNewAccount
titleField.placeholder = NSLocalizedString(@"optional", nil);
FieldSectionSpecifier *titleSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObject:titleField] title:nil description:nil];

FieldSpecifier *usernameField = [FieldSpecifier emailFieldWithKey:kAccountUsername title:NSLocalizedString(@"Email", nil) defaultValue:@""];
FieldSpecifier *passwordField = [FieldSpecifier passwordFieldWithKey:kAccountPassword title:NSLocalizedString(@"Password", nil) defaultValue:@""];
FieldSpecifier *tokenField = [FieldSpecifier textFieldWithKey:kAccountToken title:NSLocalizedString(@"Token", nil) defaultValue:@""];
FieldSpecifier *vendorIDField = [FieldSpecifier numericFieldWithKey:kAccountVendorID title:NSLocalizedString(@"Vendor ID", nil) defaultValue:@""];
vendorIDField.placeholder = @"8XXXXXXX";
FieldSpecifier *selectVendorIDButtonField = [FieldSpecifier buttonFieldWithKey:@"SelectVendorIDButton" title:NSLocalizedString(@"Auto-Fill Vendor ID...", nil)];
// FieldSpecifier *selectVendorIDButtonField = [FieldSpecifier buttonFieldWithKey:@"SelectVendorIDButton" title:NSLocalizedString(@"Auto-Fill Vendor ID...", nil)];

FieldSectionSpecifier *loginSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:usernameField, passwordField, vendorIDField, selectVendorIDButtonField, nil]
FieldSectionSpecifier *loginSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:tokenField, vendorIDField, nil]
title:NSLocalizedString(@"iTunes Connect Login", nil)
description:NSLocalizedString(@"You can import reports via iTunes File Sharing without entering your login.", nil)];

Expand All @@ -295,21 +302,19 @@ - (void)addNewAccount
- (void)editAccount:(ASAccount *)account
{
self.selectedAccount = account;
NSString *username = account.username;
NSString *password = account.password;
NSString *token = account.token;
NSString *title = account.title;
NSString *vendorID = account.vendorID;

FieldSpecifier *titleField = [FieldSpecifier textFieldWithKey:kAccountTitle title:@"Description" defaultValue:title];
titleField.placeholder = NSLocalizedString(@"optional", nil);

FieldSpecifier *usernameField = [FieldSpecifier emailFieldWithKey:kAccountUsername title:NSLocalizedString(@"Username", nil) defaultValue:username];
FieldSpecifier *passwordField = [FieldSpecifier passwordFieldWithKey:kAccountPassword title:NSLocalizedString(@"Password", nil) defaultValue:password];
FieldSpecifier *tokenField = [FieldSpecifier textFieldWithKey:kAccountToken title:NSLocalizedString(@"Token", nil) defaultValue:token];
FieldSpecifier *vendorIDField = [FieldSpecifier numericFieldWithKey:kAccountVendorID title:NSLocalizedString(@"Vendor ID", nil) defaultValue:vendorID];
FieldSpecifier *selectVendorIDButtonField = [FieldSpecifier buttonFieldWithKey:@"SelectVendorIDButton" title:NSLocalizedString(@"Auto-Fill Vendor ID...", nil)];
// FieldSpecifier *selectVendorIDButtonField = [FieldSpecifier buttonFieldWithKey:@"SelectVendorIDButton" title:NSLocalizedString(@"Auto-Fill Vendor ID...", nil)];

vendorIDField.placeholder = @"8XXXXXXX";
FieldSectionSpecifier *loginSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:usernameField, passwordField, vendorIDField, selectVendorIDButtonField, nil]
FieldSectionSpecifier *loginSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:tokenField, vendorIDField, nil]
title:NSLocalizedString(@"iTunes Connect Login", nil)
description:nil];
FieldSpecifier *loginSubsectionField = [FieldSpecifier subsectionFieldWithSection:loginSection key:@"iTunesConnect"];
Expand Down Expand Up @@ -431,33 +436,30 @@ - (void)showSettings
- (void)fieldEditor:(FieldEditorViewController *)editor didFinishEditingWithValues:(NSDictionary *)returnValues
{
if ([editor.editorIdentifier isEqualToString:kAddNewAccountEditorIdentifier] || [editor.editorIdentifier isEqualToString:kEditAccountEditorIdentifier]) {
NSString *username = [returnValues objectForKey:kAccountUsername];
NSString *password = [returnValues objectForKey:kAccountPassword];
NSString *token = [returnValues objectForKey:kAccountToken];
NSString *vendorID = [returnValues objectForKey:kAccountVendorID];
NSString *title = [returnValues objectForKey:kAccountTitle];
if ((!username || [username isEqualToString:@""]) && (!title || [title isEqualToString:@""])) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Missing Information", nil) message:NSLocalizedString(@"You need to enter at least a username or a description.\n\nIf you want to download reports from iTunes Connect, you need to enter your username and password, otherwise you can just enter a description.", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil] autorelease] show];
if (!title || [title isEqualToString:@""]) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Missing Information", nil) message:NSLocalizedString(@"You need to enter at least a description.\n\nIf you want to download reports from iTunes Connect, you need to enter an access token, otherwise you can just enter a description.", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil] autorelease] show];
return;
}
if ([editor.editorIdentifier isEqualToString:kAddNewAccountEditorIdentifier]) {
if (password && password.length > 0 && (!vendorID || vendorID.length == 0)) {
if (token && token.length > 0 && (!vendorID || vendorID.length == 0)) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Missing Information", nil) message:NSLocalizedString(@"You need to enter a vendor ID. If you don't know your vendor ID, tap \"Auto-Fill Vendor ID\".", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil] autorelease] show];
return;
}
ASAccount *account = (ASAccount *)[NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:self.managedObjectContext];
account.title = title;
account.username = username;
account.vendorID = vendorID;
account.sortIndex = [NSNumber numberWithLong:time(NULL)];
[account setPassword:password];
[account setToken:token];
}
else if ([editor.editorIdentifier isEqualToString:kEditAccountEditorIdentifier]) {
ASAccount *account = (ASAccount *)editor.context;
[account deletePassword];
account.username = username;
[account deleteToken];
account.title = title;
account.vendorID = vendorID;
[account setPassword:password];
[account setToken:token];

NSMutableDictionary *productsByID = [NSMutableDictionary dictionary];
for (Product *product in self.selectedAccount.products) {
Expand Down Expand Up @@ -544,28 +546,27 @@ - (void)fieldEditor:(FieldEditorViewController *)editor pressedButtonWithKey:(NS
otherButtonTitles:NSLocalizedString(@"Delete", nil), nil] autorelease];
confirmDeleteAlert.tag = kAlertTagConfirmDelete;
[confirmDeleteAlert show];
} else if ([key isEqualToString:@"SelectVendorIDButton"]) {
FieldEditorViewController *vc = nil;
if (self.presentedViewController) {
UINavigationController *nav = (UINavigationController *)self.presentedViewController;
vc = (FieldEditorViewController *)[[nav viewControllers] objectAtIndex:0];
} else {
vc = (FieldEditorViewController *)[self.navigationController.viewControllers lastObject];
}
NSString *username = [vc.values objectForKey:kAccountUsername];
NSString *password = [vc.values objectForKey:kAccountPassword];
[vc dismissKeyboard];

if (!username || username.length == 0 || !password || password.length == 0) {
[[[[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"Please enter your username and password first.", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil] autorelease] show];
return;
}

NSDictionary *loginInfo = [NSDictionary dictionaryWithObjectsAndKeys:password, kAccountPassword, username, kAccountUsername, nil];

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:vc.navigationController.view animated:YES];
hud.labelText = NSLocalizedString(@"Checking Vendor ID...", nil);
[hud showWhileExecuting:@selector(findVendorIDsWithLogin:) onTarget:self withObject:loginInfo animated:YES];
// } else if ([key isEqualToString:@"SelectVendorIDButton"]) {
// FieldEditorViewController *vc = nil;
// if (self.presentedViewController) {
// UINavigationController *nav = (UINavigationController *)self.presentedViewController;
// vc = (FieldEditorViewController *)[[nav viewControllers] objectAtIndex:0];
// } else {
// vc = (FieldEditorViewController *)[self.navigationController.viewControllers lastObject];
// }
// NSString *token = [vc.values objectForKey:kAccountToken];
// [vc dismissKeyboard];
//
// if (!token || token.length == 0) {
// [[[[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"Please enter your iTunes Connect access token. To create an access token, login to iTunes Connect, visit the Sales and Trends module, then click 'Sales' and click 'Reports'.", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil] autorelease] show];
// return;
// }
//
// NSDictionary *loginInfo = [NSDictionary dictionaryWithObjectsAndKeys:token, kAccountToken, nil];
//
// MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:vc.navigationController.view animated:YES];
// hud.labelText = NSLocalizedString(@"Checking Vendor ID...", nil);
// [hud showWhileExecuting:@selector(findVendorIDsWithLogin:) onTarget:self withObject:loginInfo animated:YES];
} else if ([key isEqualToString:kDownloadBoxcarButton]) {
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"boxcar://provider/965"]]) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Boxcar Already Installed", nil)
Expand Down
4 changes: 3 additions & 1 deletion Classes/AppSalesAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[self.accountsViewController reloadAccounts];
}

// since Apple uses tokens for the Reporter tool now, clean up any old passwords
[self.accountsViewController deleteAllAccountPasswords];

[[CurrencyManager sharedManager] refreshIfNeeded];

NSString* productSortByValue = [[NSUserDefaults standardUserDefaults] objectForKey:@"ProductSortby"];
Expand Down Expand Up @@ -183,7 +186,6 @@ - (BOOL)migrateDataIfNeeded
if (!account) {
account = (ASAccount *)[NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:[self managedObjectContext]];
if (oldUsername) account.username = oldUsername;
if (oldPassword) account.password = oldPassword;
}
[self saveContext];
[[ReportDownloadCoordinator sharedReportDownloadCoordinator] importReportsIntoAccount:account fromDirectory:legacyReportDirectory deleteAfterImport:YES];
Expand Down
4 changes: 2 additions & 2 deletions Classes/ReportDownloadOperation.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
@class ASAccount;

@interface ReportDownloadOperation : NSOperation {

ASAccount *_account;
NSString *username;
NSString *password;
NSString *token;
NSPersistentStoreCoordinator *psc;
NSManagedObjectID *accountObjectID;
NSInteger downloadCount;
Expand Down
10 changes: 4 additions & 6 deletions Classes/ReportDownloadOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ - (id)initWithAccount:(ASAccount *)account
self = [super init];
if (self) {
username = [[account username] copy];
password = [[account password] copy];
token = [[account token] copy];
_account = [account retain];
accountObjectID = [[account objectID] copy];
psc = [[[account managedObjectContext] persistentStoreCoordinator] retain];
Expand Down Expand Up @@ -157,15 +157,13 @@ - (void)main
}
}

NSString *escapedUsername = [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)username, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8) autorelease];
NSString *escapedPassword = [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)password, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8) autorelease];
NSString *escapedToken = [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)token, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8) autorelease];

NSURL *URL = [NSURL URLWithString:@"https://reportingitc-reporter.apple.com/reportservice/sales/v1"];

NSString *command = [NSString stringWithFormat:@"[p=Reporter.properties, Sales.getReport, %@,Sales,Summary,%@,%@]", vendorID, dateType, reportDateString];
NSDictionary *body = @{
@"userid" : escapedUsername,
@"password" : escapedPassword,
@"accesstoken" : escapedToken,
@"version" : @"2.0",
@"mode" : @"Normal",
@"queryInput" : command
Expand Down Expand Up @@ -358,7 +356,7 @@ - (NSString *)stringFromSynchronousPostRequestWithURL:(NSURL *)URL bodyDictionar
- (void)dealloc
{
[username release];
[password release];
[token release];
[accountObjectID release];
[_account release];
[psc release];
Expand Down
2 changes: 1 addition & 1 deletion Classes/SalesViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ - (void)setViewMode:(DashboardViewMode)newViewMode

- (void)downloadReports:(id)sender
{
if (self.account.password && self.account.password.length > 0) { //Only download reports for accounts with login
if (self.account.token && self.account.token.length > 0) { //Only download reports for accounts with login
if (!account.vendorID || account.vendorID.length == 0) {
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Vendor ID Missing", nil)
message:NSLocalizedString(@"You have not entered a vendor ID for this account. Please go to the account's settings and fill in the missing information.", nil)
Expand Down