Skip to content

Commit

Permalink
增加 ldap 的认证逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangkaili committed Jan 15, 2021
1 parent 371c73e commit e0cb1d2
Show file tree
Hide file tree
Showing 13 changed files with 728 additions and 128 deletions.
15 changes: 15 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ REDIS_URL=redis://localhost:6479
URL=http://localhost:3000
PORT=3000

# LDAP_URL=
LDAP_URL=ldap://127.0.0.1

# enforce (auto redirect to) https in production, (optional) default is true.
# set to false if your SSL is terminated at a loadbalancer, for example
FORCE_HTTPS=true
Expand Down Expand Up @@ -55,6 +58,16 @@ AWS_S3_FORCE_PATH_STYLE=true
# set to "public-read" to allow public access
AWS_S3_ACL=private

# aliyun oss config
ALIS_ACCESS_KEY_ID=
ALIS_SECRET_ACCESS_KEY=
ALIS_REGION=
ALIS_BUCKET=

# uploaded aliyun oss objects permission level, default is private
# set to "public-read" to allow public access
ALIS_ACL=public-read

# Emails configuration (optional)
SMTP_HOST=
SMTP_PORT=
Expand All @@ -63,6 +76,8 @@ SMTP_PASSWORD=
SMTP_FROM_EMAIL=
SMTP_REPLY_EMAIL=

TEAM_ID=

# Custom logo that displays on the authentication screen, scaled to height: 60px
# TEAM_LOGO=https://example.com/images/logo.png

Expand Down
5 changes: 5 additions & 0 deletions app/scenes/Login/Notices.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export default function Notices({ notice }: Props) {
please contact a team admin.
</NoticeAlert>
)}
{notice === "ldap_validation" && (
<NoticeAlert>
LDAP 服务验证失败,请重新尝试.
</NoticeAlert>
)}
</>
);
}
96 changes: 96 additions & 0 deletions app/scenes/Login/Service.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,40 @@ type Props = {
authUrl: string,
isCreate: boolean,
onEmailSuccess: (email: string) => void,
onLdapSuccess: (username: string) => void,
};

type State = {
showEmailSignin: boolean,
isSubmitting: boolean,
email: string,
ldapId: string,
ldapPassword: string,
showLdapSignin: boolean,
};

class Service extends React.Component<Props, State> {
state = {
showEmailSignin: false,
isSubmitting: false,
email: "",
ldapId: "",
ldapPassword: "",
showLdapSignin: false,
};

handleChangeEmail = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({ email: event.target.value });
};

handleChangeLdap = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({ ldapId: event.target.value });
};

handleChangePwdForLdap = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({ ldapPassword: event.target.value });
};

handleSubmitEmail = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();

Expand All @@ -56,6 +71,33 @@ class Service extends React.Component<Props, State> {
}
};

handleSubmitLdap = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();

if (this.state.showLdapSignin && this.state.ldapId) {
this.setState({ isSubmitting: true });

try {
// console.log("action:", event.currentTarget.action);
const response = await client.post(event.currentTarget.action, {
username: this.state.ldapId,
password: this.state.ldapPassword,
});
if (response.redirect) {
window.location.href = response.redirect;
} else {
console.log("login success for ldap");
this.props.onLdapSuccess(this.state.ldapId);
}
} finally {
console.log("submit failure");
this.setState({ isSubmitting: false });
}
} else {
this.setState({ showLdapSignin: true });
}
};

render() {
const { isCreate, id, name, authUrl } = this.props;

Expand Down Expand Up @@ -97,6 +139,60 @@ class Service extends React.Component<Props, State> {
</Wrapper>
);
}
if (id === "ldap") {
if (isCreate) {
return null;
}

console.log("id:", id);
console.log("name:", name);
console.log("authUrl:", authUrl);
console.log("ldap type");
return (
<Wrapper key="ldap">
<Form
method="POST"
action="/auth/ldap"
onSubmit={this.handleSubmitLdap}
>
{this.state.showLdapSignin ? (
<>
<InputLarge
type="text"
name="ldap"
placeholder="Please enter ldapID"
value={this.state.ldapId}
onChange={this.handleChangeLdap}
disabled={this.state.isSubmitting}
autoFocus
required
short
/>

<InputLarge
type="password"
name="password"
placeholder="Please enter password"
value={this.state.ldapPassword}
onChange={this.handleChangePwdForLdap}
disabled={this.state.isSubmitting}
autoFocus
required
short
/>
<ButtonLarge type="submit" disabled={this.state.isSubmitting}>
Sign In →
</ButtonLarge>
</>
) : (
<ButtonLarge type="submit" icon={<EmailIcon />} fullwidth>
Continue with LDAP
</ButtonLarge>
)}
</Form>
</Wrapper>
);
}

const icon =
id === "slack" ? (
Expand Down
39 changes: 34 additions & 5 deletions app/scenes/Login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,29 @@ type Props = {

type State = {
emailLinkSentTo: string,
ldapAccount: string,
};

@observer
class Login extends React.Component<Props, State> {
state = {
emailLinkSentTo: "",
ldapAccount: "",
};

handleReset = () => {
this.setState({ emailLinkSentTo: "" });
this.setState({ ldapAccount: "" });
};

handleEmailSuccess = (email) => {
this.setState({ emailLinkSentTo: email });
};

handleLdapSuccess = (ldapId) => {
this.setState({ldapAccount: ldapId});
}

render() {
const { auth, location } = this.props;
const { config } = auth;
Expand All @@ -57,7 +64,8 @@ class Login extends React.Component<Props, State> {
}

const hasMultipleServices = config.services.length > 1;
const defaultService = find(

let defaultService = find(
config.services,
(service) => service.id === auth.lastSignedIn && !isCreate
);
Expand All @@ -82,7 +90,7 @@ class Login extends React.Component<Props, State> {
<PageTitle title="Check your email" />
<CheckEmailIcon size={38} color="currentColor" />

<Heading centered>Check your email</Heading>
<Heading centered>Check your LDAP ID</Heading>
<Note>
A magic sign-in link has been sent to the email{" "}
<em>{this.state.emailLinkSentTo}</em>, no password needed.
Expand All @@ -96,6 +104,28 @@ class Login extends React.Component<Props, State> {
);
}

if (this.state.ldapAccount) {
return (
<Background>
{header}
<Centered align="center" justify="center" column auto>
<PageTitle title="Check your LDAP ID" />
<CheckEmailIcon size={38} color="currentColor" />

<Heading centered>Check your LDAP ID</Heading>
<Note>
A magic sign-in link has been sent to the account{" "}
<em>{this.state.ldapAccount}</em>.
</Note>
<br />
<ButtonLarge onClick={this.handleReset} fullwidth neutral>
Back to login
</ButtonLarge>
</Centered>
</Background>
);
}

return (
<Background>
{header}
Expand Down Expand Up @@ -134,17 +164,16 @@ class Login extends React.Component<Props, State> {
)}
</React.Fragment>
)}

{config.services.map((service) => {
if (defaultService && service.id === defaultService.id) {
return null;
}

console.log("service.id:", service.id)
return (
<Service
key={service.id}
isCreate={isCreate}
onEmailSuccess={this.handleEmailSuccess}
onLdapSuccess={this.handleLdapSuccess}
{...service}
/>
);
Expand Down
8 changes: 4 additions & 4 deletions app/utils/uploadFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ type Options = {

// ali oss
const ossClient = new AliOSS({
bucket: 'ali-bucket',
region: 'ali-region',
accessKeyId: 'ali-access-id',
accessKeySecret: 'ali-access-secret',
bucket: env.ALIS_BUCKET,
region: env.ALIS_REGION,
accessKeyId: env.ALIS_ACCESS_KEY_ID,
accessKeySecret: env.ALIS_SECRET_ACCESS_KEY,
});

export const uploadFile = async (
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@
"jszip": "^3.5.0",
"koa": "^2.10.0",
"koa-body": "^4.2.0",
"koa-bodyparser": "^4.3.0",
"koa-compress": "2.0.0",
"koa-convert": "1.2.0",
"koa-cookies": "^3.0.0",
"koa-helmet": "5.2.0",
"koa-jwt": "^3.6.0",
"koa-logger": "^2.0.1",
Expand All @@ -121,6 +123,7 @@
"koa-sendfile": "2.0.0",
"koa-sslify": "2.1.2",
"koa-static": "^4.0.1",
"ldapjs": "^2.2.3",
"lodash": "^4.17.19",
"mammoth": "^1.4.11",
"mobx": "4.6.0",
Expand All @@ -132,6 +135,7 @@
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
"polished": "3.6.5",
"promised-ldap": "^0.3.0",
"query-string": "^4.3.4",
"quoted-printable": "^1.0.1",
"randomstring": "1.1.5",
Expand Down
22 changes: 14 additions & 8 deletions server/api/attachments.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,27 @@ router.post("attachments.create", auth(), async (ctx) => {
ctx.assertPresent(size, "size is required");

const { user } = ctx.state;
const s3Key = uuid.v4();
// const s3Key = uuid.v4();
const acl =
ctx.body.public === undefined
? AWS_S3_ACL
: ctx.body.public
? "public-read"
: "private";

const bucket = acl === "public-read" ? "public" : "uploads";
const key = `${bucket}/${user.id}/${s3Key}/${name}`;
// const bucket = acl === "public-read" ? "public" : "uploads";
const bucket = "";

// const key = `${bucket}/${user.id}/${s3Key}/${name}`;
const key = `${bucket}/${user.id}/${name}`;

const credential = makeCredential();
const longDate = format(new Date(), "YYYYMMDDTHHmmss\\Z");
const policy = makePolicy(credential, longDate, acl);
const endpoint = publicS3Endpoint();
const url = `${endpoint}/${key}`;
// const url = `${endpoint}/${key}`;
const url = `${endpoint}${key}`;
console.log("bucketUrl:", url);

if (documentId) {
const document = await Document.findByPk(documentId, { userId: user.id });
Expand Down Expand Up @@ -76,10 +82,10 @@ router.post("attachments.create", auth(), async (ctx) => {
acl,
key,
policy,
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": credential,
"x-amz-date": longDate,
"x-amz-signature": getSignature(policy),
// "x-amz-algorithm": "AWS4-HMAC-SHA256",
// "x-amz-credential": credential,
// "x-amz-date": longDate,
// "x-amz-signature": getSignature(policy),
},
attachment: {
documentId,
Expand Down
Loading

0 comments on commit e0cb1d2

Please sign in to comment.