OpenSAML: Include X509 Certificate in EncyptedAssertion - saml-2.0

I am trying to implement IDP initiated SSO using OpenSAML and ComponentSpace acts as SP. The SP is configured to accept a SAMLResponse's with an encrypted Assertion.
I am able to encryt the Assertion but the SP requires me to include X509 Certificate within KeyInfo in the EncryptedData.
Is this possible using OpenSAML?
If Yes, Could you please guide me on how to achieve this?
What SP Expects:
<saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>......</X509Certificate>
</X509Data>
</KeyInfo>
<CipherData>
<CipherValue>......</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>......</CipherValue>
</CipherData>
</EncryptedData>
What I am able to generate:
<EncryptedAssertion>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Type="http://www.w3.org/2001/04/xmlenc#Element">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<xenc:CipherData>
<xenc:CipherValue>......</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
</dsig:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>......</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</EncryptedAssertion>

Check the method signSamlResponseObject2() from http://ideone.com/p4Bhy9.
public void signSamlResponseObject2() {
try {
String keyStoreFileName = "/WEB-INF/classes/saml-data/keystore.jks";
InputStream fis = getServletContext().getResource(keyStoreFileName)
.openStream();
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(fis, "abc123456*".toCharArray());
fis.close();
// Get Private Key Entry From keystore
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) ks
.getEntry("zohosso", new KeyStore.PasswordProtection(
"abc123456*".toCharArray()));
PrivateKey privKey = pkEntry.getPrivateKey();
PublicKey pubKey = ks.getCertificate("zohosso").getPublicKey();
X509Certificate cert = (X509Certificate) ks
.getCertificate("zohosso");
/*
* // Getting x509 Certificate from the keystore directly.
*
* KeyStore.TrustedCertificateEntry certEntry =
* (KeyStore.TrustedCertificateEntry) ks .getEntry("zohosso", new
* KeyStore.PasswordProtection( "abc123456*".toCharArray()));
*
* X509Certificate cert = (X509Certificate)
* certEntry.getTrustedCertificate();
*/
// Create a DOM XMLSignatureFactory that will be used to generate
// the
// enveloped signature.
// String providerName =
// System.getProperty("jsr105Provider",JSR_105_PROVIDER);
XMLSignatureFactory sigFactory = XMLSignatureFactory
.getInstance("DOM");
// Create a Reference to the enveloped document (we are
// signing the whole document, so a URI of "" signifies that) and
// also specify the SHA1 digest algorithm and the ENVELOPED
// Transform.
List envelopedTransform = Collections.singletonList(sigFactory
.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null));
Reference ref = sigFactory.newReference("",
sigFactory.newDigestMethod(DigestMethod.SHA1, null),
envelopedTransform, null, null);
SignatureMethod signatureMethod = sigFactory.newSignatureMethod(
SignatureMethod.DSA_SHA1, null);
CanonicalizationMethod canonicalizationMethod = sigFactory
.newCanonicalizationMethod(
CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
(C14NMethodParameterSpec) null);
// Create the SignedInfo
SignedInfo signedInfo = sigFactory.newSignedInfo(
canonicalizationMethod, signatureMethod,
Collections.singletonList(ref));
// Create a KeyValue containing the DSA PublicKey
KeyInfoFactory keyInfoFactory = sigFactory.getKeyInfoFactory();
KeyValue keyValuePair = keyInfoFactory.newKeyValue(pubKey);
// Creating the x509 certificate data from Certificate object ( cert )
List x509 = new ArrayList();
x509.add(cert);
X509Data x509Data = keyInfoFactory.newX509Data(x509);
// Create a KeyInfo and add the KeyValue to it
// keyInfoItems.add(Collections.singletonList(keyValuePair));
// Adding the certificate data and the key value pair to the keyInfo
List keyInfoItems = new ArrayList();
keyInfoItems.add(x509Data);
keyInfoItems.add(keyValuePair);
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoItems);
// Building the org.jdom.Document object from the samlResponse
// string
// ------------------------------------------------------------------
SAXBuilder builder = new SAXBuilder();
org.jdom.Document doc = builder.build(new ByteArrayInputStream(
strResponseXML.getBytes()));
// ------------------------------------------------------------------
// Convert the rootElement extracted from the doc to w3cElement
// ------------------------------------------------------------------
org.jdom.Element docRootElement = doc.getRootElement();
doc = docRootElement.getDocument();
XMLOutputter xmlOutputter = new XMLOutputter();
StringWriter elemStrWriter = new StringWriter();
xmlOutputter.output(doc, elemStrWriter);
byte[] xmlBytes = elemStrWriter.toString().getBytes();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
org.w3c.dom.Element w3cElement = dbf.newDocumentBuilder()
.parse(new ByteArrayInputStream(xmlBytes))
.getDocumentElement();
// --------------------------------------------------------------------
// Create a DOMSignContext and specify the DSA PrivateKey and
// location of the resulting XMLSignature's parent element
DOMSignContext dsc = new DOMSignContext(privKey, w3cElement);
// compute the correct location to insert the signature xml
// (location is important because the SAML xsd's enforce sequence on
// signed
// info.)
org.w3c.dom.Node xmlSigInsertionPoint = null;
String JSR_105_PROVIDER = "org.jcp.xml.dsig.internal.dom.XMLDSigRI";
String SAML_PROTOCOL_NS_URI_V20 = "urn:oasis:names:tc:SAML:2.0:protocol";
org.w3c.dom.NodeList nodeList = w3cElement.getElementsByTagNameNS(
SAML_PROTOCOL_NS_URI_V20, "Extensions");
if (nodeList.getLength() != 0) {
xmlSigInsertionPoint = nodeList.item(nodeList.getLength() - 1);
} else {
nodeList = w3cElement.getElementsByTagNameNS(
SAML_PROTOCOL_NS_URI_V20, "Status");
xmlSigInsertionPoint = nodeList.item(nodeList.getLength() - 1);
}
dsc.setNextSibling(xmlSigInsertionPoint);
// Marshal, generate (and sign) the enveloped signature
XMLSignature signature = sigFactory.newXMLSignature(signedInfo,
keyInfo);
signature.sign(dsc);
// Create the root dom element from the w3cElement using DOMBuilder
DOMBuilder domBuilder = new DOMBuilder();
org.jdom.Element signedElement = domBuilder.build(w3cElement);
doc.setRootElement((org.jdom.Element) signedElement.detach());
xmlOutputter = new XMLOutputter();
strFinalResponse = xmlOutputter.outputString(doc);
System.out.println("The signed SAML Response is : "
+ strFinalResponse);
} catch (Exception e) {
System.out
.println("Exception while attempting to sign the SAML Response.");
e.printStackTrace();
}
}

Related

Validate SAML Response

I have a SAML response and few other data. Based on this, I need to validate if the response has been tampered or not. How can I do that?
What all I have?
SAML Response with Signed Message & Assertion
IdP EntityId
SP EntityId
SP ACS Endpoint
Target URL
Certificate of the IdP in X509 format.
Language needed: JAVA
Got a solution. If anyone is loking for it.
try {
InputStream is = new FileInputStream("<CERTIFICATE FILE>");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(is);
X509Certificate x509Certificate = (X509Certificate) cert;
PublicKey pk = x509Certificate.getPublicKey();
BasicX509Credential publicCredential = new BasicX509Credential();
publicCredential.setPublicKey(pk);
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
SignableSAMLObject signableSAMLObject = (SignableSAMLObject) <XML OBJECT>;
Signature signature = signableSAMLObject.getSignature();
signatureValidator.validate(signature);
}catch(Exception ex){
// fail this.
}
XML Object can be obtained from SAML Message using marshaller in following way:
String encodedMessage = request.getParameter(PARAM_SAML);
String decodedMessage = new String(Base64.decodeBase64(encodedMessage.getBytes()));
DefaultBootstrap.bootstrap();
BasicParserPool ppMgr = new BasicParserPool();
ppMgr.setNamespaceAware(true);
Document responseRoot = ppMgr.parse(new StringReader(decodedMessage));
UnmarshallerFactory unmarshallFactory = Configuration.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallFactory.getUnmarshaller(responseRoot.getDocumentElement());
XMLObject obj = unmarshaller.unmarshall(responseRoot.getDocumentElement());

Using ComponentSpace SAML to sign and post request

I am using component space libraries to create SAML request and sign it then post it to URL, However request is not posting successfully because i need to use RSA algorithm key but so far i found that it is not available in SamlKeyAlgorithm also i need to change the Key size to 2048 below are my method that i used to send the request.
private void SendTawtheeqRequest()
{
string ConsumerServiceUrl = "https://tawtheeq.sa:8443/identity-gateway-test/ReceiveSAMLRequest";
// Create a SAML response object.
Response samlResponse = new Response();
// Assign the consumer service url.
samlResponse.Destination = ConsumerServiceUrl;
Issuer issuer = new Issuer(GetAbsoluteUrl("~/"));
samlResponse.Issuer = issuer;
samlResponse.Status = new Status(SamlPrimaryStatusCode.Success, null);
Assertion samlAssertion = new Assertion();
samlAssertion.Issuer = issuer;
// Use the local user's local identity.
Subject subject = new Subject(new NameId(User.Identity.Name));
SubjectConfirmation subjectConfirmation = new SubjectConfirmation(SamlSubjectConfirmationMethod.Bearer);
SubjectConfirmationData subjectConfirmationData = new SubjectConfirmationData();
subjectConfirmationData.Recipient = ConsumerServiceUrl;
subjectConfirmation.SubjectConfirmationData = subjectConfirmationData;
subject.SubjectConfirmations.Add(subjectConfirmation);
samlAssertion.Subject = subject;
// Create a new authentication statement.
AuthnStatement authnStatement = new AuthnStatement();
authnStatement.AuthnContext = new AuthnContext();
authnStatement.AuthnContext.AuthnContextClassRef = new AuthnContextClassRef(SamlAuthenticationContext.Password);
samlAssertion.Statements.Add(authnStatement);
X509Certificate2 encryptingCert = new X509Certificate2(Path.Combine(HttpRuntime.AppDomainAppPath, "my-bank1-public.cer"));
EncryptedAssertion encryptedSamlAssertion = new EncryptedAssertion(samlAssertion, encryptingCert, new EncryptionMethod(SamlKeyAlgorithm.TripleDesCbc));
samlResponse.Assertions.Add(encryptedSamlAssertion);
samlResponse.Assertions.Add(samlAssertion);
samlResponse.SendHttpPost(Response.OutputStream, ConsumerServiceUrl, "10");
}

Spring ldap unlocking an account

I am trying to unlock user account using spring ldap and getting the error message
""Malformed 'LockoutTime' attribute value" exception.
My code looks like below
public boolean unlockAccount(Name dn) {
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("lockoutTime", 0));
ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
return true;
}
I am using Windows server 2016 and Spring ldap 2.3.2.
Is 'lockoutTime' the correct attribute to unlock an account ?
Is there anything else I am missing ?
In LDAP if you type the wrong password for more than 5 times, the account gets locked. If you want to unlock the user you have to delete an operational attribute name as pwdAccountLockedTime.
public String unlockUser(Users pvo) {
System.out.println("this is pvo" + pvo);
Name dn = buildDn(pvo);
DirContextOperations context = ldapTemplate.lookupContext(dn);
ModificationItem[] modificationItems;
modificationItems = new ModificationItem[1];
modificationItems[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
new BasicAttribute("pwdAccountLockedTime"));
ldapTemplate.modifyAttributes(dn, modificationItems);
return "Account Unlocked";
}
build Dn for your LDAP and use the above code then the user gets unlocked.
String[] attrIDs = new String[] { "lockoutTime", "sAMAccountName",
"distinguishedName","pwdLastSet", "accountExpires", "userAccountControl",
"IsAccountLocked" };
ctls.setReturningAttributes(attrIDs);
ctls.setSearchScope(2);
String filter = "(&(objectClass=user)(objectCategory=Person)(sAMAccountName=" +
samaccountname+ "))";
NamingEnumeration<SearchResult> answer = ctx.search(adManagedOU, filter,ctls);
while (answer.hasMore()) {
SearchResult rs = answer.next();
Attributes attrs = rs.getAttributes();
distinguishedName = rs.getNameInNamespace();
String[] lockouttime = null;
String lockOutValue=attrs.get("lockoutTime");
if (lockOutValue != null)
{
lockouttime = attrs.get("lockoutTime").toString().split(":");
if (Long.valueOf(lockouttime[1].trim()) > 0) {
ModificationItem[] mods1 = new ModificationItem[] {
new ModificationItem(2, new BasicAttribute("lockoutTime", "0") )
};
((DirContext) ctls).modifyAttributes(distinguishedName, mods1);
}
else
{
LOGGER.info(username + " Account Not Locked");
}
The only values that may be set on lockouttime is to set the value to "0" which will effectively un-lock the account.
To learn more on Microsoft Active Directory Lockouts.
Setting the value to a String instead of an int makes this work, at least with AWS Simple AD.
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("lockoutTime", "0"));
ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});

How do I write a test class for Messaging.SingleEmailMessage apex class?

I have written the below Apex class that processes incoming email that are sent to an email service address and creates a new task from the incoming mail and then associates this new task with a matching record in salesforce. The match is done on the record name and incoming email subject. The class also sends an email notifying the "Assigned to" user that they have received a reply on a request they are working on.
This works perfect in Sandbox but I have no experience writing test classes. Can anyone advise how I write the test class for the below?
global class RequestEmailHandler implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
String myPlainText = email.plainTextBody;
String subject = email.fromName + ' - ' + email.subject;
system.debug(email);
subject = subject.left(255);
Request__c request;
if (subject != null && subject.trim().length() > 0 && subject.indexOf('(R-') > 0) {
Integer idx = subject.indexOf('(R-');
String requestName = subject.substring(idx+1, subject.indexOf(')', idx));
request = [SELECT Id, Assigned_To__c FROM Request__c WHERE Name = :requestName];
}
if (request == null) {
result.message = 'We were unable to locate the associated request.This may be due to the unique "R" number being removed from the subject line.\n Please include the original email subject when replying to any emails.';
result.success = false;
return result;
}
// Add the email plain text into the local variable
Task task = new Task(
WhatId = request.Id,
Description = myPlainText,
Priority = 'Normal',
Status = 'Completed',
Type = 'Email',
Subject = subject,
ActivityDate = System.today(),
RecordTypeId = '01250000000HkEw');
insert task;
//Find the template
EmailTemplate theTemplate = [select id, name from EmailTemplate where DeveloperName = 'New_Email_Reply2'];
//Create a new email right after the task
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
//Add email To addresses to list
List<String> toAddresses = new List<String>();
toAddresses.add(email.fromAddress);
//Set the list of to addresses
mail.setToAddresses(toAddresses);
//Set the template id
mail.setTemplateId(theTemplate.id);
//The Id of the user
mail.setTargetObjectId(request.Assigned_To__c);
//Set the id of the request
mail.setWhatId(request.Id);
//If you need the email also saved as an activity, otherwise set to false
mail.setSaveAsActivity(false);
//Send Email
try {
Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
}
catch (EmailException e) {
system.debug('sendEmail error: ' + e.getMessage());
}
// Save attachments, if any
if (email.textAttachments != null)
{
for(Messaging.Inboundemail.TextAttachment tAttachment : email.textAttachments)
{
Attachment attachment = new Attachment();
attachment.Name = tAttachment.fileName;
attachment.Body = Blob.valueOf(tAttachment.body);
attachment.ParentId = request.Id;
insert attachment;
}
}
//Save any Binary Attachment
if (email.binaryAttachments != null)
{
for(Messaging.Inboundemail.BinaryAttachment bAttachment : email.binaryAttachments) {
Attachment attachment = new Attachment();
attachment.Name = bAttachment.fileName;
attachment.Body = bAttachment.body;
attachment.ParentId = request.Id;
insert attachment;
return result;
}
}
return result;
}
}
Below is my attempt which is only getting 24% coverage. I know it is missing vital code but I do not know enough about test classes to take it any further.
Can anyone assist?
Test Class
#isTest
public class testforemail {
static testMethod void insertRequest() {
Request__c requestToCreate = new Request__c();
requestToCreate.Subject__c= 'test';
requestToCreate.Requested_By_Email__c= 'graham.milne#fmr.com';
insert requestToCreate;
Messaging.InboundEmail email = new Messaging.InboundEmail();
Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope();
Request__c testRequest = [select Id,Name from Request__c limit 1];
System.debug(testRequest.Name);
email.subject = (testRequest.Name);
email.fromName = 'test test';
email.plainTextBody = 'Hello, this a test email body. for testing purposes only.Phone:123456 Bye';
Messaging.InboundEmail.BinaryAttachment[] binaryAttachments = new Messaging.InboundEmail.BinaryAttachment[1];
Messaging.InboundEmail.BinaryAttachment binaryAttachment = new Messaging.InboundEmail.BinaryAttachment();
binaryAttachment.Filename = 'test.txt';
String algorithmName = 'HMacSHA1';
Blob b = Crypto.generateMac(algorithmName, Blob.valueOf('test'),
Blob.valueOf('test_key'));
binaryAttachment.Body = b;
binaryAttachments[0] = binaryAttachment ;
email.binaryAttachments = binaryAttachments ;
envelope.fromAddress = 'user#fmr.com';
// Add the email plain text into the local variable
Task task = new Task(
WhatId = (testRequest.Id),
Description = email.plainTextBody,
Priority = 'Normal',
Status = 'Completed',
Type = 'Email',
Subject = (testRequest.Name),
ActivityDate = System.today(),
RecordTypeId = '01250000000HkEw');
insert task;
// setup controller object
RequestEmailHandler catcher = new RequestEmailHandler();
Messaging.InboundEmailResult result = catcher.handleInboundEmail(email, envelope);
System.assertEquals( true,result.success );
}
}
The first step is to identify what lines of code are not being covered by your test class.
If you're using Eclipse, you can see this from the Apex Test Runner View.
Or, you can see this from the Developer console as well.
Another thing you need to consider is the isolation of your DML operations in a separate utility class.
public class TestUtils
{
// create request objects method here
// create task objects method here
}
Also, check your debug logs and make sure your code is not throwing any exceptions (i.e., null pointer exceptions, DML exceptions, etc.)
You must also add assertions to check that your code is behaving as expected.
Hope this helps.
The main thing you need to do is to test as many use cases as you can via unit tests.
So, setup data for specific case and run you email processing. After email, check the result using System.assertEquals(). Make separate tests for each use case.
Then, if you don't hit at least 75%, check what is not covered. You, probably, either don't need that code (in case you covered all use cases), or don't write a test for use case, which uses those lines of code.

Excel doc contents to webservice

I have a wpf staff creation window in which I can create basic information like first name, last name etc this creates the staff in my REST web service. An example:
Client side:
private void CreateStaffMember_Click(object sender, RoutedEventArgs e)
{
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + this.textBox1.Text + "</FirstName>");
sb.AppendLine("<LastName>" + this.textBox2.Text + "</LastName>");
sb.AppendLine("<Password>" + this.passwordBox1.Password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Web Service side:
#region POST
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/Staff")]
void AddStaff(Staff staff);
#endregion
public void AddStaff(Staff staff)
{
staff.StaffID = (++eCount).ToString();
staff.Salt = GenerateSalt();
byte[] passwordHash = Hash(staff.Password, staff.Salt);
staff.Password = Convert.ToBase64String(passwordHash);
staffmembers.Add(staff);
}
All fine on that side, but Im looking to "import" the staff details from an excel spreadsheet, not sure if import is the correct word but I want to take the first names and last names contained in such n such spreadsheet and add them to the web service from the client side wpf application.
How would I go about it? I have my open file dialog:
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
}
}
So I open my excel spread sheet then how would I go about taking the inner contents and sending it to the web service? Really stuck on the code or how to go about it :/
Just looking for an automated way of adding staff members rather than manually typing the names, but seeing as the staff excel doc could be named anything I wanted the open file dialog box. The structure inside will always be the same first name then last name.
First, here is my test Excel file that contains the Staff you want to import:
(Column 'A' if first name, column 'B' is last name and column 'C' is the password...)
Ok, so assuming that your code calling your web service works, here is my version of the Import_Click method (and a generic method to save new staff):
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
Microsoft.Office.Interop.Excel.Application vExcelObj = new Microsoft.Office.Interop.Excel.Application();
try
{
Workbook theWorkbook = vExcelObj.Workbooks.Open(filename, Type.Missing, true);
Worksheet sheet = theWorkbook.Worksheets[1]; // This is assuming that the list of staff is in the first worksheet
string vFirstName = "temp";
string vLastName = "temp";
string vPassword = "temp";
int vIndex = 1;
while (vFirstName != "")
{
// Change the letters of the appropriate columns here!
// In my example, 'A' is first name, 'B' is last name and 'C' is the password
vFirstName = sheet.get_Range("A" + vIndex.ToString()).Value.ToString();
vLastName = sheet.get_Range("B" + vIndex.ToString()).Value.ToString();
vPassword = sheet.get_Range("C" + vIndex.ToString()).Value.ToString();
this.SaveNewStaff(vFirstName, vLastName, vPassword);
vIndex++;
}
}
catch (Exception ex)
{
MessageBox.Show("Error processing excel file : " + ex.Message);
}
finally {
vExcelObj.Quit();
}
}
}
private void SaveNewStaff(string firstName, string lastName, string password) {
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + firstName + "</FirstName>");
sb.AppendLine("<LastName>" + lastName + "</LastName>");
sb.AppendLine("<Password>" + password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
//MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Note: I have REMed out the MessageBox in the call to the web service to make sure you are not annoyed by it if the list is long, but you are free to "unREM" it if you need confirmation for every staff creation. In the same line of taught, there is not validation that the creation has occurred successfully. I would need more details to create a decent validation process.
Also VERY important, this does not validate if the staff you are saving already exists in the list. If you re-run this import procedure multiple times, it may (and probably will) create duplicate entries.
Cheers

Resources