Last active
July 25, 2018 06:47
-
-
Save cburnette/bce6236076a15aeb7c03 to your computer and use it in GitHub Desktop.
Upload file from local disk to a specific Box folder with auto-refreshing tokens using encrypted tokens in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Box.V2; | |
using Box.V2.Auth; | |
using Box.V2.Config; | |
using Box.V2.Exceptions; | |
using Box.V2.Models; | |
using Nito.AsyncEx; | |
using System.IO; | |
using System.Security.Cryptography; //make sure you add a reference to system.security | |
namespace MoveFileFromDiskToBox | |
{ | |
//Make sure you install the NuGet package 'Box Windows SDK V2' | |
class Program | |
{ | |
const string CLIENT_ID = "YOUR_CLIENT_ID"; | |
const string CLIENT_SECRET = "YOUR_CLIENT_SECRET"; | |
const string TOKEN_FILENAME = "Data.dat"; | |
const string TOKEN_FILE_LENGTH_FILENAME = "Data_length.dat"; | |
const char TOKEN_SPLIT_CHAR = '|'; | |
const string BOX_FOLDER_PATH = "/Personal Backups/Test"; //for this example code, make sure this folder structure exists in Box | |
//set these to point to whatever file you want to upload; make sure it exists! | |
const string PATH_TO_FILE = "C:\\"; | |
const string FILENAME = "example.pdf"; | |
static string access_token; | |
static string refresh_token; | |
static BoxClient client; | |
static void Main(string[] args) | |
{ | |
if (args.Length == 1) | |
{ | |
//If tokens are passed in as args assume they are valid; this is the way to bootstrap the refresh cycle. | |
//You only need to do this the first time you run the program and then the tokens will automatically refresh across runs. | |
//Use the tool at this URL to generate your first set of tokens: https://box-token-generator.herokuapp.com/ | |
//There should be one command line arg that looks like: ACCESS_TOKEN|REFRESH_TOKEN | |
var tokens = args[0].Split(TOKEN_SPLIT_CHAR); | |
access_token = tokens[0]; | |
refresh_token = tokens[1]; | |
} | |
else | |
{ | |
//After you have bootstrapped the tokens you should not send anything in as command line args. | |
//The program will read the encrypted token file, decrypt the tokens, refresh the tokens, and then create a new session to Box. | |
//read in the length of the data to decrypt | |
int bytesToRead = int.Parse(File.ReadAllText(TOKEN_FILE_LENGTH_FILENAME, UnicodeEncoding.ASCII)); | |
//we need to read the tokens from the encrypted token file | |
using (FileStream fStream = new FileStream(TOKEN_FILENAME, FileMode.Open)) | |
{ | |
byte[] decryptedData = DecryptDataFromStream(null, DataProtectionScope.CurrentUser, fStream, bytesToRead); | |
string decryptedString = UnicodeEncoding.ASCII.GetString(decryptedData); | |
var tokens = decryptedString.Split(TOKEN_SPLIT_CHAR); | |
access_token = tokens[0]; | |
refresh_token = tokens[1]; | |
} | |
} | |
//http://blog.stephencleary.com/2012/02/async-console-programs.html | |
try | |
{ | |
var config = new BoxConfig(CLIENT_ID, CLIENT_SECRET, new Uri("http://localhost")); | |
var session = new OAuthSession(access_token, refresh_token, 3600, "bearer"); | |
client = new BoxClient(config, session); | |
AsyncContext.Run(() => MainAsync()); | |
} | |
catch (Exception ex) | |
{ | |
Console.Error.WriteLine(ex); | |
} | |
Console.WriteLine(); | |
Console.Write("Press return to exit..."); | |
Console.ReadLine(); | |
} | |
static async Task MainAsync() | |
{ | |
//first we need to refresh the tokens and persist them in a file | |
var newSession = await client.Auth.RefreshAccessTokenAsync(access_token); | |
//store the tokens on disk with encryption. You don't want your tokens exposed to other users | |
WriteTokensEncrypted(newSession.AccessToken, newSession.RefreshToken); | |
//look up the folder id based on the path | |
var boxFolderId = await FindBoxFolderId(BOX_FOLDER_PATH); | |
using (FileStream fs = File.Open(PATH_TO_FILE + FILENAME, FileMode.Open, FileAccess.Read)) | |
{ | |
Console.WriteLine("Uploading file..."); | |
// Create request object with name and parent folder the file should be uploaded to | |
BoxFileRequest request = new BoxFileRequest() | |
{ | |
Name = DateTime.Now.ToFileTime() + "-" + FILENAME, //for this demo, don't collide with any other files | |
Parent = new BoxRequestEntity() { Id = boxFolderId } | |
}; | |
BoxFile f = await client.FilesManager.UploadAsync(request, fs); | |
} | |
} | |
static async Task<String> FindBoxFolderId(string path) | |
{ | |
var folderNames = path.Split('/'); | |
folderNames = folderNames.Where((f) => !String.IsNullOrEmpty(f)).ToArray(); //get rid of leading empty entry in case of leading slash | |
var currFolderId = "0"; //the root folder is always "0" | |
foreach (var folderName in folderNames) | |
{ | |
var folderInfo = await client.FoldersManager.GetInformationAsync(currFolderId); | |
var foundFolder = folderInfo.ItemCollection.Entries.OfType<BoxFolder>().First((f) => f.Name == folderName); | |
currFolderId = foundFolder.Id; | |
} | |
return currFolderId; | |
} | |
private static void WriteTokensEncrypted(string access_token, string refresh_token) | |
{ | |
//store the tokens in an encrypted file for later retrieval; | |
//http://msdn.microsoft.com/en-US/library/ms229741(v=vs.110).aspx | |
byte[] toEncrypt = UnicodeEncoding.ASCII.GetBytes(access_token + TOKEN_SPLIT_CHAR + refresh_token); | |
File.Delete(TOKEN_FILENAME); //blow away any old file | |
FileStream fStream = new FileStream(TOKEN_FILENAME, FileMode.OpenOrCreate); | |
int bytesWritten = EncryptDataToStream(toEncrypt, null, DataProtectionScope.CurrentUser, fStream); | |
fStream.Close(); | |
//write the length of bytes that were encrypted; we need this when decrypting | |
File.WriteAllText(TOKEN_FILE_LENGTH_FILENAME, bytesWritten.ToString(), UnicodeEncoding.ASCII); | |
} | |
public static int EncryptDataToStream(byte[] Buffer, byte[] Entropy, DataProtectionScope Scope, Stream S) | |
{ | |
int length = 0; | |
// Encrypt the data in memory. | |
byte[] encryptedData = ProtectedData.Protect(Buffer, Entropy, Scope); | |
// Write the encrypted data to a stream. | |
if (S.CanWrite && encryptedData != null) | |
{ | |
S.Write(encryptedData, 0, encryptedData.Length); | |
length = encryptedData.Length; | |
} | |
// Return the length that was written to the stream. | |
return length; | |
} | |
public static byte[] DecryptDataFromStream(byte[] Entropy, DataProtectionScope Scope, Stream S, int Length) | |
{ | |
byte[] inBuffer = new byte[Length]; | |
byte[] outBuffer; | |
// Read the encrypted data from a stream. | |
if (S.CanRead) | |
{ | |
S.Read(inBuffer, 0, Length); | |
outBuffer = ProtectedData.Unprotect(inBuffer, Entropy, Scope); | |
} | |
else | |
{ | |
throw new IOException("Could not read the stream."); | |
} | |
// Return the length that was written to the stream. | |
return outBuffer; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment